如何模拟ServiceStackController Cache属性?

时间:2017-06-14 12:50:38

标签: asp.net-mvc unit-testing caching mocking servicestack

提供以下ServiceStack控制器

public class MyController : ServiceStackController 
{
    public ActionResult Index()
    {
        return View(Cache.GetAllKeys());
    }
}

以及以下测试类

[TestFixture]
public class MyControllerTests 
{
    [Test]
    public void Should_call_cache()
    {
        var controller = new MyController();
        // Mock here to access Cache, otherwise throws NullReferenceException
        var result = controller.Index();
        Assert.IsNotNull(result);
        var model = result.Model as IEnumerable<string>;
        Assert.IsNotNull(model);
    }
}

模拟ICacheClient Cache属性以验证测试方法的正确方法是什么?

2 个答案:

答案 0 :(得分:3)

更新:

正如OP在评论中所述。模拟被测对象的做法通常是避免的。然而,由于底层类的设计(IMO)内部与实现问题紧密相关,难以单独测试,因此解决方法是使用允许的内容覆盖问题成员以便在进行测试时获得更多控制权。

Cache一个只读虚拟属性,但它可以在派生类中重写。使用它作为模拟所需功能的入口点。

创建一个受测试类的派生类,并覆盖Cache属性以返回一个行为符合预期的模拟。

在以下示例中,Moq用于模拟主题控制器并覆盖Cache虚拟属性。

public void _Should_call_cache() {
    //Arrange
    var controller = Mock.Of<MyController>();

    var keys = new[] { "key1", "key2", "key3" };
    var cacheMock = new Mock<ICacheClient>();
    cacheMock.Setup(_ => _.GetAllKeys()).Returns(keys);

    var mockController = Mock.Get(controller);
    mockController.CallBase = true;
    mockController.Setup(_ => _.Cache).Returns(cacheMock.Object);

    //Act
    var result = controller.Index() as ViewResult;

    //Assert
    Assert.IsNotNull(result);
    var model = result.Model as IEnumerable<string>;
    Assert.IsNotNull(model);
}

我查看了ServiceStackController.cs,意识到可以覆盖readonly属性。

答案 1 :(得分:2)

当ServiceStack之外的testing many ServiceStack components你需要将它包装在AppHost中时,无论是用于集成测试的SelfHost还是用于单元测试,你都可以在内存AppHost中使用:

using (var appHost = new BasicAppHost {
        ConfigureContainer = c => ...,
    }.Init()) 
{
    // Test ServiceStack Components...
}

您可以使用ConfigureContainer注册ServiceStack使用的任何依赖项,因为Cache Provider ServiceStack默认使用MemoryCacheClient,因此您不需要注册任何内容。