如何配置模拟对象?

时间:2018-06-26 21:59:30

标签: c# .net unit-testing mocking moq

我有一个可启动计时器的模拟对象,需要在测试中将其丢弃。处置模拟对象的正确方法是什么?在我要嘲笑的课堂上,我有:

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing)
        {
            // Stop and dispose timer here.
        }

        _disposed = true;
    }
}

public void Dispose()
{
    Dispose(true);
}

所以现在在我的测试中,我需要模拟对象,使用它,然后确保将其丢弃。我知道我可以设置CallBase = true,但是我不确定这是否是正确的(行业标准)处理方式:

[TestMethod]
public void TestSomething()
{
    var mock = new Mock<ObjectWithTimer>() { CallBase = true };

    using (var foo = mock.Object)
    {
        foo.DoSomething(); // This consequently starts a timer.
    } // Here, Dispose() will be called.
}

using块结束时,将调用Dispose(),它会调用基础Dispose(bool)。但是,是否可以在不向我的模拟中添加CallBase = true的情况下进行此处理?当我需要模拟来覆盖其他方法时,这是不理想的。

2 个答案:

答案 0 :(得分:0)

如果您松开两个类之间的耦合,也许可以解决问题,从而使模拟更加通用。如果您的被测类不直接使用ObjectWithTimer,而是使用ObjectWithTimer实现的接口,则可以这样做。

示例:

public interface IObjectWithTimer : IDisposable
{
    void DoSomething();
}

public class ObjectWithTimer : IObjectWithTimer
{
    // ...
}

public class ClassUnderTest
{ 
    public ClassUnderTest(IObjectWithTimer timer)
    {
        // ...
    }

    public void ThisShouldCallDisposeOnTimer()
    {
        // ...
    }
}

然后您的测试代码如下:

[TestMethod]
public void ShouldCallDispose()
{
    var mock = new Mock<IObjectWithTimer>();
    var classUnderTest = new ClassUnderTest(mock.Object);

    classUnderTest.ThisShouldCallDisposeOnTimer();

    mock.Verify(x => x.Dispose(), Times.Once());
}

这样,您的测试就完全与计时器分配逻辑分离了,这实际上不是您要测试的。

答案 1 :(得分:0)

通过使用new Mock<ObjectWithTimer>(),您的代码没有与该类的实现完全分开,因为该方法提供了一个从您的具体实现派生的模拟类,而该类(如您已经明确地知道的那样)不是真的是您想要您的模拟物做什么。

ObjectWithTimer创建一个接口。该接口应包括有关该类的所有公开内容。由于类为IDisposable,因此您的接口应从该接口派生。将依赖于该类的代码改为依赖于接口,并更改测试以模拟接口而不是类。

现在您使用的是正确的Mock对象,您的测试可能需要定义该模拟的预期行为。使用模拟对象时,这是正常现象,正确模拟行为很重要,否则您的测试将一文不值(当实际代码可能失败时,测试可能会通过)。

现在,调用代码与该接口的实现已完全分离,因此不再需要具体实现类(ObjectWithTimer)。