什么是严格模拟和非严格模拟?

发布于 2024-09-07 12:23:45 字数 108 浏览 5 评论 0原文

我已经开始使用最小起订量进行模拟。有人可以向我解释严格和非严格模拟的概念吗?它们如何用于最小起订量?

编辑: 在什么场景下我们使用哪种类型的mock?

I have started using moq for mocking. Can someone explain me the concept of strict and non-strict mocks? How can they can be used in moq?

edit:
in which scenario do we use which type of mock?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

素年丶 2024-09-14 12:23:45

我不确定最小起订量具体是多少,但以下是严格模拟在 Rhino 中的工作原理。我声明我期望在我的对象 foo 上调用 foo.Bar

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

如果调用代码确实如此

foo.Bar();

,那么我就很好,因为完全满足了期望。

但是,如果调用代码是:

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

那么我的期望失败了,因为我没有明确期望调用 foo.Quux

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

Rhino 的创建者 建议您避免使用严格模拟(并且更喜欢存根),因为您通常不希望测试在收到上述意外调用时失败。当您必须修复数十个依赖于精确原始行为的测试时,它会使重构代码变得更加困难。

I'm not sure about moq specifically, but here's how strict mocks work in Rhino. I declare that I expect a call to foo.Bar on my object foo:

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

If the calling code does

foo.Bar();

then I'm fine because the expectations are exactly met.

However, if the calling code is:

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

then my expectation failed because I did not explicitly expect a call to foo.Quux.

To summarize, a strict mock will fail immediately if anything differs from the expectations. On the other hand, a non-strict mock (or a stub) will gladly "ignore" the call to foo.Quux and it should return a default(T) for the return type T of foo.Quux.

The creator of Rhino recommends that you avoid strict mocks (and prefer stubs) because you generally don't want your test to fail when receiving an unexpected call as above. It makes refactoring your code much more difficult when you have to fix dozens of test that relied on the exact original behavior.

坐在坟头思考人生 2024-09-14 12:23:45

曾经遇到过Given/When/Then吗?

  • 给定上下文
  • 当我执行某些事件时
  • ,应该会出现一个结果

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

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

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

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

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

在过去的四年中,我还没有找到一个需要使用严格模拟的示例 - 这要么是我想要验证的结果(即使我验证了它被调用的次数),要么是我可以验证的上下文告诉我是否对所提供的信息做出了正确的反应。因此,回答你的问题:

  • 非严格模拟:通常
  • 严格模拟:最好从不

注意:我强烈偏向 BDD,所以硬核 TDD 可能不同意我的观点,并且这将适合他们的工作方式。

Ever come across Given / When / Then?

  • Given a context
  • When I perform some events
  • Then an outcome should occur

This pattern appears in BDD's scenarios, and is also relevant for unit tests.

If you're setting up context, you're going to use the information which that context provides. For instance, if you're looking up something by Id, that's context. If it doesn't exist, the test won't run. In this case, you want to use a NiceMock or a Stub or whatever - Moq's default way of running.

If you want to verify an outcome, you can use Moq's verify. In this case, you want to record the relevant interactions. Fortunately, this is also Moq's default way of running. It won't complain if something happens that you weren't interested in for that test.

StrictMock is there for when you want no unexpected interactions to occur. It's how old-style mocking frameworks used to run. If you're doing BDD-style examples, you probably won't want this. It has a tendency to make tests a bit brittle and harder to read than if you separate the aspects of behaviour you're interested in. You have to set up expectations for both the context and the outcome, for all outcomes which will occur, regardless of whether they're of interest or not.

For instance, if you're testing a controller and mocking out both your validator and your repository, and you want to verify that you've saved your object, with a strict mock you also have to verify that you've validated the object first. I prefer to see those two aspects of behaviour in separate examples, because it makes it easier for me to understand the value and behaviour of the controller.

In the last four years I haven't found a single example which required the use of a strict mock - either it was an outcome I wanted to verify (even if I verify the number of times it's called) or a context for which I can tell if I respond correctly to the information provided. So in answer to your question:

  • non-strict mock: usually
  • strict mock: preferably never

NB: I am strongly biased towards BDD, so hard-core TDDers may disagree with me, and it will be right for the way that they are working.

_失温 2024-09-14 12:23:45

这是一篇很好的文章
我通常最终会得到这样的结果

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 个协作者使用严格的模拟来测试登录(用户名)。我不明白如何永远不应该使用严格模拟。

Here's a good article.
I usually end up having something like this

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);
    }

}

...and I use Strict mocks for the 3 collaborators to test login(username). I don't see how Strict Mocks should never be used.

无畏 2024-09-14 12:23:45

我有一个简单的约定:

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

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

例如:
假设我们有数据库提供程序 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.

}

I have a simple convention:

  1. Use strict mocks when the system under test (SUT) is delegating the call to the underlying mocked layer without really modifying or applying any business logic to the arguments passed to itself.

  2. Use loose mocks when the SUT applies business logic to the arguments passed to itself and passes on some derived/modified values to the mocked layer.

For eg:
Lets say we have database provider StudentDAL which has two methods:

Data access interface looks something like below:

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

The implementation which consumes this DAL looks like below:

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.

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文