什么是严格和非严格的嘲笑?

时间:2010-06-28 16:13:14

标签: unit-testing mocking moq

我已经开始使用moq进行模拟。有人能解释我严格和非严格嘲笑的概念吗?它们如何在moq中使用?

修改 在哪种情况下我们使用哪种类型的模拟?

4 个答案:

答案 0 :(得分:13)

我不确定moq具体,但这里是严格的模拟在Rhino中的工作方式。我声明我希望在我的对象foo.Bar上调用foo

foo.Expect(f => f.Bar()).Returns(5);

如果调用代码

foo.Bar();

然后我很好,因为完全符合预期。

但是,如果调用代码是:

foo.Quux(12);
foo.Bar();

然后我的期望失败了,因为我没有明确期望拨打foo.Quux

总而言之,如果有任何不同于期望的话,严格的模拟会立即失败。另一方面,非严格模拟(或存根)将很乐意“忽略”对foo.Quux的调用,并且应该为default(T)返回T foo.Quux 1}}。

Rhino的创建者recommends that you avoid strict mocks(并且更喜欢存根)因为您通常不希望您的测试在接收到如上所述的意外调用时失败。当您必须修复依赖于完全原始行为的数十个测试时,它会使您的代码重构变得更加困难。

答案 1 :(得分:3)

偶然遇到过Given / When / Then?

  • 给出上下文
  • 当我执行某些活动时
  • 然后应该出现结果

此模式出现在BDD的场景中,也与单元测试相关。

如果您正在设置上下文,那么您将使用该上下文提供的信息。例如,如果您通过Id查找某些内容,那就是上下文。如果它不存在,则测试将不会运行。在这种情况下,您希望使用NiceMock或Stub或其他 - Moq的默认运行方式。

如果您想验证结果,可以使用Moq的验证。在这种情况下,您希望记录相关的交互。幸运的是,这也是Moq默认的运行方式。如果您对该测试不感兴趣的事情,它不会抱怨。

当您不希望发生意外交互时,就会出现StrictMock。这是旧式模拟框架的运行方式。如果你正在做BDD风格的例子,你可能不会想要这个。与您分离您感兴趣的行为方面相比,它倾向于使测试变得有点脆弱且难以阅读。您必须对上下文和结果建立期望,对于将要发生的所有结果,无论如何他们是否感兴趣。

例如,如果您正在测试控制器并模拟验证器和存储库,并且想要验证是否已保存对象,则必须验证是否已经验证首先是对象。我更喜欢在单独的示例中看到行为的这两个方面,因为它使我更容易理解控制器的价值和行为。

在过去的四年里,我还没有找到一个需要使用严格模拟的例子 - 要么是我想要验证的结果(即使我验证它被调用的次数),要么是如果我对提供的信息做出正确回答,我可以告诉他们。所以回答你的问题:

  • 非严格模拟通常
  • 严格模拟:最好不要
NB:我对BDD非常偏向,所以硬核TDD可能不同意我的意见,而且他们的工作方式也是正确的。

答案 2 :(得分:0)

这是一个很好的article 我通常最终得到类似的东西

public class TestThis {

    private final Collaborator1 collaborator1;
    private final Collaborator2 collaborator2;
    private final Collaborator2 collaborator3;

    TestThis(Collaborator1 collaborator1, Collaborator2 collaborator2, Collaborator3 collaborator3) {
        this.collaborator1 = collaborator1;
        this.collaborator2 = collaborator2;
        this.collaborator3 = collaborator3;
    }

    public Login login(String username) {
        User user = collaborator1.getUser(username);
        collaborator2.notify(user);
        return collaborator3.login(user);
    }

}

...我使用严格模拟为3个合作者测试登录(用户名)。我不知道如何永远不要使用Strict Mocks。

答案 3 :(得分:0)

我有一个简单的约定:

  1. 当被测系统(SUT)将调用委托给底层模拟层时,使用严格的模拟,而不是真正修改或应用传递给自身的参数的任何业务逻辑。

  2. 当SUT将业务逻辑应用于传递给自身的参数并将一些派生/修改的值传递给模拟层时,使用松散的模拟。

  3. 例如: 假设我们有数据库提供程序StudentDAL,它有两种方法:

    数据访问界面如下所示:

    public Student GetStudentById(int id);
    public IList<Student> GetStudents(int ageFilter, int classId);
    

    使用此DAL的实现如下所示:

    public Student FindStudent(int id)
    {
       //StudentDAL dependency injected
       return StudentDAL.GetStudentById(id);
       //Use strict mock to test this
    }
    public IList<Student> GetStudentsForClass(StudentListRequest studentListRequest)
    {
      //StudentDAL dependency injected
      //age filter is derived from the request and then passed on to the underlying layer
      int ageFilter = DateTime.Now.Year - studentListRequest.DateOfBirthFilter.Year;
      return StudentDAL.GetStudents(ageFilter , studentListRequest.ClassId)
      //Use loose mock and use verify api of MOQ to make sure that the age filter is correctly passed on.
    
    }