Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you mock ServiceStack ISession using Moq and StructureMap?

I'm using ServiceStack / StructureMap / Moq. The service makes a call to Session, which is type ServiceStack.CacheAccess.ISession. For unit tests, I created a Mock object using Moq, and added it to the StructureMap configuration:

protected Mock<ISession> sessionMock = new Mock<ISession>();
ObjectFactory.Configure(
    cfg =>
       {
           cfg.For<ISession>().Use(sessionMock.Object);

However, I was not surprised when the Session object was null -- I'm pretty sure I'm leaving out a step. What else do I need to do to fill my Session property with a mock object?

[EDIT] Here's a simple test scenario

Code to test. Simple request / service

[Route("getKey/{key}")]
public class MyRequest:IReturn<string>
{
    public string Key { get; set; }
}

public class MyService:Service
{
    public string Get(MyRequest request)
    {
        return (string) Session[request.Key];
    }
}

The base test class and MockSession classes

// test base class
public abstract class MyTestBase : TestBase
{
    protected IRestClient Client { get; set; }

    protected override void Configure(Container container)
    {
        // this code is never reached under any of my scenarios below
        container.Adapter = new StructureMapContainerAdapter();
        ObjectFactory.Initialize(
            cfg =>
                {
                    cfg.For<ISession>().Singleton().Use<MockSession>();
                });
    }
}

public class MockSession : ISession
{
    private Dictionary<string, object> m_SessionStorage = new Dictionary<string, object>();

    public void Set<T>(string key, T value)
    {
        m_SessionStorage[key] = value;
    }

    public T Get<T>(string key)
    {
        return (T)m_SessionStorage[key];
    }

    public object this[string key]
    {
        get { return m_SessionStorage[key]; }
        set { m_SessionStorage[key] = value; }
    }
}

And tests. See comments for where I'm seeing the failure. I didn't really expect versions 1 & 2 to work, but hoped version 3 would.

[TestFixture]
public class When_getting_a_session_value:MyTestBase
{
    [Test]
    public void Test_version_1()
    {
        var session = ObjectFactory.GetInstance<MockSession>();
        session["key1"] = "Test";
        var request = new MyRequest {Key = "key1"};
        var client = new MyService();  // generally works fine, except for things like Session
        var result = client.Get(request);  // throws NRE inside MyService
        result.ShouldEqual("Test");
    }

    [Test]
    public void Test_version_2()
    {
        var session = ObjectFactory.GetInstance<MockSession>();
        session["key1"] = "Test";
        var request = new MyRequest {Key = "key1"};
        var client = ObjectFactory.GetInstance<MyService>();
        var result = client.Get(request);  // throws NRE inside MyService
        result.ShouldEqual("Test");
    }
    [Test]
    public void Test_version_3()
    {
        var session = ObjectFactory.GetInstance<MockSession>();
        session["key1"] = "Test";
        var request = new MyRequest {Key = "key1"};
        var client = CreateNewRestClient();
        var result = client.Get(request);  // throws NotImplementedException here
        result.ShouldEqual("Test");
    }
}
like image 765
John Mathis Avatar asked Feb 20 '13 15:02

John Mathis


1 Answers

It looks like you're trying to create unit tests, but you're using an AppHost like you wound an Integration test. See this previous answer for differences between the two and docs on Testing.

You can mock the Session by registering an instance in Request.Items["__session"], e.g:

[Test]
public void Can_mock_IntegrationTest_Session_with_Request()
{
    using (new BasicAppHost(typeof(MyService).Assembly).Init())
    {
        var req = new MockHttpRequest();
        req.Items[SessionFeature.RequestItemsSessionKey] = 
            new AuthUserSession {
                UserName = "Mocked",
            };

        using (var service = HostContext.ResolveService<MyService>(req))
        {
            Assert.That(service.GetSession().UserName, Is.EqualTo("Mocked"));
        }
    }
}

Otherwise if you set AppHost.TestMode=true ServiceStack will return the IAuthSession that's registered in your IOC, e.g:

[Test]
public void Can_mock_UnitTest_Session_with_IOC()
{
    var appHost = new BasicAppHost
    {
        TestMode = true,
        ConfigureContainer = container =>
        {
            container.Register<IAuthSession>(c => new AuthUserSession {
                UserName = "Mocked",
            });
        }
    }.Init();

    var service = new MyService {
        Request = new MockHttpRequest()
    };
    Assert.That(service.GetSession().UserName, Is.EqualTo("Mocked"));

    appHost.Dispose();
}
like image 75
mythz Avatar answered Oct 27 '22 04:10

mythz