How do you mock ServiceStack ISession using Moq and StructureMap?
Asked Answered
P

1

5

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");
    }
}
Paint answered 20/2, 2013 at 15:12 Comment(3)
I think it's because I don't have the StructureMap adaptor configured properly, when unit testing. Not sure how to do that...Paint
Can you show all the code for the test? i.e. so we can see how you're calling the service?Lawless
Added code that shows the problem and how I tried to solve it.Paint
L
10

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[Keywords.Session], e.g:

[Test]
public void Can_mock_IntegrationTest_Session_with_Request()
{
    using var appHost = new BasicAppHost(typeof(MyService).Assembly).Init();
    
    var req = new MockHttpRequest();
    req.Items[Keywords.Session] = 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()
{
    using 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"));
}
Lawless answered 21/2, 2013 at 21:10 Comment(6)
Thanks for the response! I was so focused on thinking I had to create the service using one of the SS methods I didn't look into manually filling ISessionFactory.Paint
np :) ServiceStack is pretty much binded together with simple clean classes and interfaces, so most of the time the easiest thing will work :)Lawless
Worked great, too! Changed it oonly a little to work with StructureMap.Paint
@Lawless I'm running into a Null Reference Exception in one of my tests (your comment on line 14 mentions it)... is there any way to get ahold of actual mocked session properties from within the service?Merci
I found this when looking to mock a session, and saw that you were going to look to simplify the effort required. Did you ever look at this? If so, is there a new strategy? Thanks.Counter
@Counter see updated answer with how I would mock the Session now.Lawless

© 2022 - 2024 — McMap. All rights reserved.