如何模拟ServiceStackController Cache属性?

lab*_*lbe 6 asp.net-mvc caching unit-testing mocking servicestack

提供以下ServiceStack控制器

public class MyController : ServiceStackController 
{
    public ActionResult Index()
    {
        return View(Cache.GetAllKeys());
    }
}
Run Code Online (Sandbox Code Playgroud)

和以下测试类

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Nko*_*osi 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);
}
Run Code Online (Sandbox Code Playgroud)

我查看了ServiceStackController.cs,发现 readonly 属性可以被覆盖。