Moq Mocking Indexer Properties

Mocking an indexer property with Moq is nearly but not quite straightforward. SetUpProperty() doesn't work but if you only need to moq write/read to one or two keys or you can use this code from a stackoverflow post by seanlinmt for each key:

httpContext.SetupSet(x => x.Session["key"] = It.IsAny<object>())
        .Callback((string name, object value) =>
        {
           httpContext.SetupGet(x => x.Session[name]).Returns(value);
        });

The missing functionality is that you have to setup for each specific key you are going to access. You can't do this to setup all in one:

httpContext.SetupSet(x => x.Session[ /**/ It.IsAny<string>() /**/ ] 
    = It.IsAny<object>()). ....

If you're mocking out SessionState or ApplicationState you may want to mock multiple keys even for a single test fixture, but don't want dozens of lines of setup code for each key you use.

You can at least abbreviate by shipping out the setup to an extension method so you can do this:

mockSessionStateBase.SetUpIndexerForKeys("a", "b", "c");

Like this:

public static class MoqStateDictionaryExtension
{
    private static Dictionary<Mock, Dictionary<string, object>> FakeStateDictionaries= new Dictionary<Mock, Dictionary<string, object>>();

    public static void SetUpIndexerForKeys(this Mock<HttpApplicationStateBase> @this, params string[] keys)
    {
        EnsureDictionary(@this);
        foreach (var key in keys)
        {
            string key1 = key;
            @this.SetupSet(
                    x => x[key1] = It.IsAny<object>()
                )
                .Callback<string, object>(
                    (name, val) =>
                    {
                        FakeStateDictionaries[@this][name] = val;
                        @this.SetupGet(x => x[name]).Returns(FakeStateDictionaries[@this][name]);
                    }
                );
        }
    }

    public static void SetUpIndexerForKeys(this Mock<HttpSessionStateBase> @this, params string[] keys)
    {
        EnsureDictionary(@this);
        foreach (var key in keys)
        {
            string key1 = key;
            @this.SetupSet(
                    x => x[key1] = It.IsAny<object>()
                )
                .Callback<string, object>(
                    (name, val) =>
                    {
                        FakeStateDictionaries[@this][name] = val;
                        @this.SetupGet(x => x[name]).Returns(FakeStateDictionaries[@this][name]);
                    }
                );
        }
    }

    private static void EnsureDictionary(Mock mock)
    {
        if (!FakeStateDictionaries.ContainsKey(mock)) { FakeStateDictionaries[mock] = new Dictionary<string, object>(); }
    }
    public static void RemoveDictionary(Mock mock)
    {
        if (FakeStateDictionaries.ContainsKey(mock)) { FakeStateDictionaries.Remove(mock); }
    }
    public static void RemoveDictionaries()
    {
        FakeStateDictionaries = new Dictionary<Mock, Dictionary<string, object>>();
    }
}

Used like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Moq;
using NUnit.Framework;

namespace MockingIndexersWithMoq
{
    [TestFixture]
    public class ApplicationWrapperTests
    {
        private Mock<HttpApplicationStateBase> mockApplication;
        private HttpApplicationStateBase exampleUsage;

        [SetUp]
        public void SetUp()
        {
            mockApplication = new Mock<HttpApplicationStateBase>();
            exampleUsage = mockApplication.Object;
        }

        [TearDown]
        public void ClearFakeStateDictionaries()
        {
            MoqStateDictionaryExtension.RemoveDictionary(mockApplication);
        }

        [Test]
        public void TestWhichReliesOnMockedIndexer()
        {
            mockApplication.SetUpIndexerForKeys("a", "b", "c");

            exampleUsage["a"] = 1;
            exampleUsage["a"].ShouldEqual(1);
            exampleUsage["a"] = 2;
            exampleUsage["a"].ShouldEqual(2);
            exampleUsage["b"] = 1;
            exampleUsage["b"].ShouldEqual(1);
            exampleUsage["c"].ShouldBeNull();
            exampleUsage["d"].ShouldBeNull();
            exampleUsage["e"].ShouldBeNull();
        }
    }
}

dotCover config file for command line NUnit test coverage

So you want to produce a coverage report for your .Net project, preferably from the command line? If you use dotCover and NUnit then this:

<?xml version="1.0" encoding="utf-8"?>
<AnalyseParams>
    <Executable><!-- Path to your NUnit bin directory e.g.  -->C:\Program Files\NUnit 2.5.10\bin\net-2.0\nunit-console.exe</Executable>
    <WorkingDir><!-- This path works for running dotCover with a config file in the project directory-->bin\Debug\</WorkingDir>
    <Arguments><!-- The dlls containing NUnit tests. Space delimited if more than one-->My.Tests.dll My.MoreTests.dll</Arguments>
    <Output><!-- Path to where I want the report. Can be relative or absolute -->My.Tests.Coverage.html</Output>
    <Filters>
        <IncludeFilters>
            <FilterEntry><!--  _ "Module" means project _ --><ModuleMask>*</ModuleMask></FilterEntry>
        </IncludeFilters>
        <ExcludeFilters>
            <FilterEntry><!--  _ "Module" means project _ --><ModuleMask>My.Tests</ModuleMask></FilterEntry>
            <FilterEntry><!-- namespaces can be filter with a ClassMask with * wildcard --><ClassMask>Namespaces.For.AutogeneratedCode.*</ClassMask></FilterEntry>
            <FilterEntry><ClassMask>SomeUntestable.Class</ClassMask></FilterEntry>
        </ExcludeFilters>
    </Filters>
    <ReportType>html</ReportType>
 </AnalyseParams>

will allow you, from the command line, to type:

dotCover analyse MyConfigFileName.xml

and generate coverage reports. Assuming that dotCover is in your path of course.
I like to set the filters to exclude coverge report on the test project itself as well as autogenerated code.

Covering Multiple Test Projects In One Run

  • Set your working directory to be a parent of all the test projects, e.g. the solution directory.
  • List the full relative paths to each Test dll, space limited:
    <WorkingDir>.</WorkingDir>
    <Arguments>Web.Tests\bin\Debug\MyProject.Web.Tests.dll Implementation.Tests\bin\Debug\MyProject.Implementation.Tests.dll</Arguments>

Filtering and more advanced coverage configs

  • Look down the right hand side of the page here : http://www.jetbrains.com/dotcover/documentation/index.html for documentation, such as it is.
  • Filtering is covered here: http://blogs.jetbrains.com/dotnet/2010/07/filtering-with-dotcover/
  • More complex stuff is touched on here: http://blogs.jetbrains.com/dotnet/tag/code-coverage/

Use SqlExpress or Sql Server for Asp.Net Session

Problems using Sql Server or Sql Express for Asp.Net Session State

So you've run aspnet_regsql.exe -S .\SqlExpress -E -ssadd -sstype p or something like that, after following instructions about Asp.Net Session State Modes (in particular noting the instructions about the Agent XP setting for SqlExpress if you're using SqlExpress) and you've set your web.config sessionState section; and you hoped that aspnet_regsql might do something useful like give permissions to your machine's AspNet account to use the session state and it would all Just Work. Aaah. You can hope.

Error messages when setting up Asp.Net to use SqlServer Session State

Cannot open database requested in login 'ASPState'. Login failed for user {MachineName}\ASPNET

Unable to use SQL Server because ASP.NET version 2.0 Session State is not installed on the SQL server. Please install ASP.NET Session State SQL Server version 2.0 or above

Failed to login to session state SQL server for user 'AspNetSqlLogin'

Example Web.Config section

<sessionState
    cookieless="false"
    regenerateExpiredSessionId="true"
    mode="SQLServer"
    timeout="30"
    sqlConnectionString="Data Source=.\SqlExpress;Integrated Security=SSPI;"
    stateNetworkTimeout="30">

Solution: Give permissions on Sql Server to your AspNet account

Note the username from your error page telling you what account Asp.Net is using to login to your server. Then, in Sql Management Studio or somesuch:

  1. Create a login for that Windows user
  2. Add the login as a user to your AspState database
  3. Give permissions to that user in that database by making it a member of the owner role

I first tried making the AspNet account a member of the db_reader & db_writer roles, but that didn't work; making it a database owner did work.

But I put SessionState on a Sql Server instance on a different machine

Then you're hopefully running your servers within a domain and you can set Asp.Net to run under a domain account, and you can give permissions on Sql server to that domain account.
Or, you create a new Sql Server login. Similar to the above process, but instead of finding an existing Windows login in Sql Management Studio, you create a new Sql Server login). The you specify a connection string with a Sql Security instead of Integrated Security: sqlConnectionString="Data Source=.\SqlExpress;User Id=SqlLoginForAspNet;Password=XXXXXX;

But I get a

Failed to login to session state SQL server for user

and

Login failed for user 'AspNetSqlLogin'. The user is not associated with a trusted SQL Server connection

That's because your Sql Server has been configured for Windows Authentication Mode only. Configure it for both Windows and Sql Server Authentication Mode via the Database Properties - Security tab.
When the Gui tells you to restart Sql Server you can do so like this:
[dos]net stop "Sql Server (SQLExpress)"
net start "Sql Server (SQLExpress)"[/dos]
Or do it from Control Panel -- Services.

Visual Studio creates unwanted directories for a new solution

So, all you want is to create a new visual studio project called MyProject in a SINGLE directory called MyProject. But behold, Visual Studio has created a million levels of directories called \MyProject\MyProject\MyProject for your project. And put it all all in source control to boot.
Simples:

  1. Specify your project name
  2. Specify the parent directory for MyProject
  3. Untick 'Create Solution Directory

Visual Studio Create Project dialog highlighting the input fields for project name, existing parent directory and the unticked Create Solution Directory option
Visual Studio Create Project dialog

Voila.