更好的使用Moq进行测试的设置方式

4

我已经使用moq进行一段时间的模拟测试,并且经常发现自己像这样做:

假设我想要测试的行为是classUnderTest.DoSomething()调用了l.Fatal("My test message")一次。

//arrange
mockLogger.Setup(l => l.Fatal("My test message"));

//act
classUnderTest.DoSomething()

//assert
mockLogger.Verify(l => l.Fatal("My test message"), Times.Once());

我觉得验证调用总是在设置的基础上重复,只不过加上了Times参数。 我真的很想知道其他人使用不同的mocking框架时是怎么做的。 有没有更好的方法?

4个回答

4

只有在实际需要控制Mock对象的行为时,例如返回内容时,您才需要进行设置:

_mockRepo.SetUp(m => m.DoStuff()).Returns(someObject);

或者抛出异常:
_mockRepo.SetUp(m => m.DoStuff()).Throws(new SomeExceptionType());

我猜在这个例子中,你将logger的mock对象传递给了某个正在测试的其他对象,在这种情况下,移除设置调用不会产生任何影响,因为只要创建Mock对象即可完成相当于设置的操作。
public class Dude : IDude
{
    private IAirSupport _support;

    public Dude(IAirSupport support)
    {
        _support = support;
    }

    public void Advance(Place place)
    {
        if(place.IsUnderAttack)
        {
            _support.CoveringFire(place);
            MoveAndFire(place);
        }

    }
}

为了模拟这个:
var support = new Mock<IAirSupport>();

var dude = new Dude(support.Object);

var place = new HotSpot { IsUnderFire = true };


dude.Advance(place);

support.Verify(m => m.CoveringFire(place), Times.Once());

这就是你所需的 - Verify完成了所有繁琐的工作,无需调用设置。

我实际上想测试记录器和被测试类之间的交互。抱歉,我应该表述得更清楚。我已经编辑了问题。现在更清晰了吗? - dalcantara
好的,你是在测试日志记录器是否被调用(而传递的消息内容并不重要),还是在测试日志记录器是否被调用,并且你关心传递给它的消息内容? - Greg Smith
Greg - 是的,在这种情况下我想要测试方法和传递给方法的参数。我知道记录器示例过于简单,但我希望它展示了我想要实现的内容。 - dalcantara

2
我同意你的看法,这似乎有些重复。但是,在这种情况下,我认为你不需要调用Setup(),因为你没有返回任何东西。如果我需要基于返回对象进行断言,我通常只会调用Setup()
最近,我开始使用StructureMap自动模拟,这促使我编写了一些基类上的快捷/实用方法来访问自动生成的模拟对象。然后,我还能写一个快捷方式来调用Verify()。它可能可以为你节省一两个按键... http://evolutionarydeveloper.blogspot.co.uk/2012/10/automock-with-structuremap-and-moq.html

你难道不想确保两个类之间的交互是正确的吗?在这种情况下(过于简化),我希望确保在调用DoSomething()时发生的某些异常被ClassUnderTest捕获并记录为致命错误。 - dalcantara

1

尝试像这样重新排列您的测试

   //arrange
   var mockLogger = new Mock<ILogger>();
   var classUnderTest = new Foo(mockLogger.Object);

   //act
   classUnderTest.DoSomething();

   //assert
   mockLogger.Verify(l => l.Fatal("My test message"), Times.Once());

这样你就不需要双重断言或重复的测试数据。基本上,我建议使用Setup提前设置预期调用来进行严格的模拟,或者使用Verify语法。个人而言,我更喜欢验证语法,因为被断言的内容非常清晰明了。

0

我同意很多测试都感觉重复,但我不知道怎么能比你描述的更容易地完成。

所有单元测试都遵循这个模式:设置-执行-断言(至少是好的测试)。这正是你的例子所优雅展示的。你可以争论如何设置和断言,但你已经用一行代码完成了每个步骤,要找到一个更好的方法是很难的。


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