如何使用Moq和StructureMap模拟ServiceStack的ISession接口?

5

我正在使用ServiceStack / StructureMap / Moq。 服务调用Session,它的类型为ServiceStack.CacheAccess.ISession。 在单元测试中,我使用Moq创建了一个Mock对象,并将其添加到StructureMap配置中:

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

然而,当Session对象为空时我并不感到惊讶 -- 我相信我肯定漏掉了某一步。那么我需要做什么才能用模拟对象填充我的Session属性呢?

[编辑] 这里是一个简单的测试场景

要进行测试的代码。简单的请求/服务

[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];
    }
}

基本测试类和MockSession类
// 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; }
    }
}

还有测试。请参见评论,了解我看到失败的地方。我并没有真正期望版本1和2能够工作,但希望版本3能够。

[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");
    }
}

我认为这是因为我在单元测试时没有正确配置StructureMap适配器,不确定如何做到这一点... - John Mathis
你能展示测试的所有代码吗?例如,我们可以看到你是如何调用服务的? - mythz
添加了展示问题及我尝试解决问题的代码。 - John Mathis
1个回答

10

看起来你正在尝试创建单元测试,但是你使用了像集成测试一样的AppHost。请查看之前的答案以了解两者之间的区别有关测试的文档

你可以通过在Request.Items[Keywords.Session]中注册一个实例来模拟会话,例如:

[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"));              
}

否则,如果您设置AppHost.TestMode=true,ServiceStack将返回在您的IOC中注册的IAuthSession,例如:
[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"));
}

谢谢您的回复!我一直在想着必须使用SS方法之一来创建服务,所以没有考虑手动填充ISessionFactory。 - John Mathis
ServiceStack基本上是由简单干净的类和接口绑定在一起的,因此大多数情况下最简单的方法都能起作用 :) - mythz
非常好用!只做了一点点修改就能与StructureMap兼容。 - John Mathis
@mythz 我在我的一个测试中遇到了空引用异常(您在第14行的评论中提到了它)...有没有办法从服务“内部”获取实际模拟的会话属性? - Brian
我在寻找模拟会话时发现了这个,并看到你将要尝试简化所需的工作。你有没有考虑过这个?如果有,是否有新的策略?谢谢。 - TortillaCurtain
@TortillaCurtain 看一下我更新的答案,里面展示了我如何模拟会话(Session)。 - mythz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接