【问题标题】:Better way to setup test with Moq使用 Moq 设置测试的更好方法
【发布时间】:2012-11-11 19:10:23
【问题描述】:

我一直在嘲笑使用 m​​oq 并且总是发现自己在做这样的事情:

假设我要测试的行为是 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 参数。即使使用不同的模拟框架,我也很想知道其他人在做什么。有一个更好的方法吗?

【问题讨论】:

    标签: unit-testing testing mocking tdd moq


    【解决方案1】:

    如果您确实需要控制 Mock 对象的行为,例如返回内容,则只需对 Mock 对象进行设置:

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

    或抛出异常:

    _mockRepo.SetUp(m => m.DoStuff()).Throws(new SomeExceptionType());
    

    我假设,在您的示例中,您将 logger 模拟对象传递给其他正在测试的对象,在这种情况下,删除 setup 调用将无效,因为只需创建 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());
    

    这就是您所需要的 - 验证完成所有繁重的工作,无需调用设置。

    【讨论】:

    • 我确实想测试记录器和 classUnderTest 之间的交互。对不起,我应该更清楚。我编辑了这个问题。更有意义吗?
    • 好的,您是在简单地测试记录器是否被调用(并且调用它的消息无关紧要),还是在测试记录器是否被调用并且您关心以下消息被传递给它?
    • Greg - 是的,在这种情况下,我想测试方法和传递给方法的参数。我知道记录器示例过于简单,但我希望它能显示我想要完成的工作。
    【解决方案2】:

    我同意您的观点,即感觉重复。但是,在这种特殊情况下,我认为您不需要致电Setup(),因为您没有返回任何内容。如果我需要根据返回的对象断言某些内容,我通常只调用Setup()

    最近我开始通过结构映射使用 AutoMocking,这促使我在基类上编写了一些快捷方式/实用方法来访问自动生成的模拟。然后我也可以编写一个快捷方式来调用Verify()。它可能会为您节省一两次击键...http://evolutionarydeveloper.blogspot.co.uk/2012/10/automock-with-structuremap-and-moq.html

    【讨论】:

    • 你不是也想确保两个类之间的交互是正确的吗?在这种情况下(过于简化),我想确保在调用 DoSomething() 期间发生的一些异常被 ClassUnderTest 捕获并记录为致命的。
    【解决方案3】:

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

       //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 语法。我个人更喜欢 verify 语法,因为 beeing 断言的内容非常清楚。

    【讨论】:

      【解决方案4】:

      我同意很多测试感觉是重复的,但我不认为事情可以比你描述的更容易完成。

      所有单元测试都遵循以下模式:setup-execute-assert(至少是好的)。这正是您的示例优雅地所做的。你可以争论你是如何设置的,你是如何断言的,但是你已经在一行代码中完成了每一个,并且很难找到一种方法来达到顶峰。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-10
        • 1970-01-01
        相关资源
        最近更新 更多