【问题标题】:How to dispose a mock object?如何处理模拟对象?
【发布时间】:2018-06-26 21:59:29
【问题描述】:

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

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 的情况下进行此处理?当我需要模拟来覆盖其他方法时,这并不理想。

【问题讨论】:

  • 再次阅读您的帖子,我不太确定您是想测试另一个使用 ObjectWithTimer 的类,还是 ObjectWithTimer 类本身?
  • 嗨,Desty。在这种情况下,我想测试 ObjectWithTimer 类本身。
  • 我还没弄明白,如果要测试ObjectWithTimer类,为什么要mock呢?

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


【解决方案1】:

如果你松开两个类之间的耦合,也许问题会得到解决,这样模拟就更通用了。如果您的测试类不直接使用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());
}

这样你的测试就完全脱离了定时器的处理逻辑,这真的不是你想要测试的。

【讨论】:

  • 这对找到解决方案很有帮助。失去类之间的耦合让我可以更好地正确测试它们。
【解决方案2】:

通过使用new Mock&lt;ObjectWithTimer&gt;(),您的代码并没有与该类的实现完全分离,因为该方法提供了一个从您的具体实现派生的模拟类,这(正如您已经清楚地解决的那样)并不是真正的你希望你的模拟正在做。

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

既然您使用的是正确的 Mock 对象,您的测试可能需要定义该模拟的预期行为方式。这在使用模拟对象时很正常,正确模拟行为很重要,否则您的测试将毫无价值(当真实代码可能失败时,它可能会通过)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多