简体   繁体   English

Mocking的用途

[英]The Purpose of Mocking

What is the purpose of mocking? mocking 的用途是什么?

I have been following some ASP.NET MVC tutorials that use NUnit for testing and Moq for mocking. I am a little unclear about the mocking part of it though.我一直在关注一些 ASP.NET MVC 教程,这些教程使用 NUnit 进行测试,使用 Moq 进行 mocking。不过我对其中的 mocking 部分不太清楚。

The purpose of mocking is to isolate the class being tested from other classes. mocking的作用是为了将被测试的class与其他类隔离

This is helpful when a class:这在 class 时很有用:

  • connects to an external resource (FileSystem, DB,.network... )连接到外部资源(文件系统、数据库、.网络...)
  • is expensive to setup, or not yet available (hardware being developed)设置昂贵,或尚不可用(正在开发硬件)
  • slows down the execution of the unit tests减慢单元测试的执行
  • has a non-deterministic behavior具有不确定的行为
  • has (or is) a user interface有(或是)用户界面

It also makes it easier to test for error conditions, as your build your mock object so that it returns and error, throw an exception...它还可以更轻松地测试错误条件,因为您构建模拟 object 以便它返回并出错,抛出异常...

The mock can record how it was invoked (function calls order, parameters) and this can be verified by the test. mock 可以记录它是如何被调用的(函数调用顺序、参数),这可以通过测试来验证。 EDIT: For instance: The method you're testing sends a message, such as an IPC.编辑:例如:您正在测试的方法发送一条消息,例如 IPC。 The method of the mock object can record how many times it was invoked, the parameter he received (ie the message to be sent). mock object 的方法可以记录它被调用了多少次,他收到的参数(即要发送的消息)。 Then the test can interrogate the mock object and assert on the number of messages sent, the content of the message... Likewise, the mock object can record the methods that are called in a log string and the test can retrieve that string and assert on it.然后测试可以询问模拟 object 并断言发送的消息数量、消息的内容...同样,模拟 object 可以记录在日志字符串中调用的方法,测试可以检索该字符串并断言在上面。

Do not abuse of mock objects : test the behaviour rather than the implementation, or the unit tests will be too tightly coupled to the code, and brittle (break at refactoring).不要滥用模拟对象:测试行为而不是实现,否则单元测试将与代码紧密耦合,并且很脆弱(在重构时中断)。

Mock can be coded manually, or generated by a mocking framework . Mock 可以手动编码,也可以由mocking 框架生成。

Mocking allows you to isolate your class under test from its dependencies. Mocking 允许您将被测 class 与其依赖项隔离开来。 Generally, you create a mock for each dependency for the class under test and set up the mock to return expected values.通常,您为被测 class 的每个依赖项创建一个模拟,并设置模拟以返回预期值。 You then provide the mock to your class under test instead of a real copy of the class that your class under test is dependent on.然后,您向被测 class 提供模拟,而不是被测 class 所依赖的 class 的真实副本。 You can then use the mocking framework to check that the expected calls were made to the mock object(s) to ensure that your class under test is operating correctly.然后,您可以使用 mocking 框架来检查是否对模拟对象进行了预期的调用,以确保被测 class 正常运行。

Its designed to poke fun at an individual instance from a group collection.它旨在取笑一组集合中的单个实例。 Used a lot at irregular object gatherings.在不定期的 object 聚会上用的很多。

While mocking is usually understood as allowing for isolation of a class under test this isn't the main point of a mock (stubs are better for this).虽然 mocking 通常被理解为允许隔离被测 class,但这不是模拟的要点(存根对此更好)。 Instead we need to look at what happens when an object is told to do something which is one of 3 things..相反,我们需要看看当 object 被告知做某事时会发生什么,这是三件事之一..

  1. Direct Output - result of a method call直接 Output - 方法调用的结果
  2. Internal Changes - changes to the class during a method call内部更改 - 在方法调用期间对 class 的更改
  3. Indirect Output - code under test calls a different class间接 Output - 被测代码调用不同的 class

State based testing is all about #1 and #2.基于 State 的测试都是关于#1 和#2 的。 #1 via looking at the result that the method gives you. #1 通过查看该方法给你的结果。 #2 by accessing the objects internal state. #2 通过访问内部对象 state。

This leaves us with #3 for which there are two avenues we can take.这给我们留下了#3,我们可以采取两种途径。 The first is a by using a Mock and the second by using a Test Spy.第一个是使用 Mock,第二个是使用 Test Spy。 The main difference is that on a Mock you create the expectations before executing the code under test and then have the mock verify them after, whereas with a Test Spy you execute the code under test and then ask the Test Spy if certain actions occurred.主要区别在于,在 Mock 上,您在执行被测代码之前创建期望,然后让模拟验证它们,而在 Test Spy 上,您执行被测代码,然后询问 Test Spy 是否发生了某些操作。

So to sum all that up.. when you think about testing what a class does if you need to test indirect output (aka a call to another class) that is where Mocking comes into play.所以总结一下.. 当你考虑测试 class 的作用时,如果你需要测试间接 output(也就是调用另一个类),这就是 Mocking 发挥作用的地方。

I'm new to mocking as well but I'll take a stab at this.我也是 mocking 的新手,但我会尝试一下。 In my experience mocking has two main benefits:根据我的经验,mocking 有两个主要好处:

  • You can start working with objects before actually writing the implementation.您可以在实际编写实现之前开始使用对象。 You can define an interface and use mocking to work with the interface in unit tests or even code.您可以定义一个接口并使用 mocking 在单元测试甚至代码中使用该接口。
  • Mocking allows you to isolate the object under test in unit tests. Mocking 允许您在单元测试中隔离被测 object。 With mock objects, you can completely control any objects that the object under test interacts with, therefore removing external dependencies from the test.使用模拟对象,您可以完全控制被测 object 与之交互的任何对象,从而从测试中消除外部依赖性。

"Mock" is a heavily overloaded term in testing & TDD circles. “模拟”在测试和 TDD 圈子中是一个重载的术语。 See Martin Fowler's article Mocks Aren't Stubs .请参阅 Martin Fowler 的文章Mocks Aren't Stubs A "proper" mock knows what values it's supposed to receive and lets you know when it doesn't get what was intended;一个“适当的”模拟知道它应该接收什么值,并在它没有得到预期的值时让您知道; this allows you to do interaction testing instead of state testing - you verify that the class under test is passing the correct messages to its collaborators, in the correct sequence.这允许您进行交互测试而不是 state 测试 - 您验证被测 class 是否以正确的顺序将正确的消息传递给它的合作者。 Interaction testing is quite different from conventional state testing and can be hard to get your head around.交互测试与传统的 state 测试有很大不同,可能很难理解。 Keeping in mind that interaction testing is the point of mocks may make them easier to understand.请记住,交互测试是模拟的重点可能会使它们更容易理解。

One other answer:另一个答案:

  • Stub = fake objects to be able to run your test without the entire real context Stub = 能够在没有整个真实上下文的情况下运行测试的假对象

  • Mock = fake object to record the interaction of your component and verify theses interactions Mock = fake object 记录组件的交互并验证这些交互

You can have several stubs in one test but only one mock because if you have more than one mock you certainly test more than one feature (and it defeat the purpose of the test-one-thing principle).你可以在一个测试中有多个存根,但只有一个模拟,因为如果你有多个模拟,你肯定会测试多个功能(并且它违背了测试一件事原则的目的)。

To go beyond the basics, Mocks are more behavioral verification than the traditionnal state verification of the test where you check the state of your component after acting on it (Arrange,Act, Assert with Mocks it is more Arrange, Act, Verify):对于超越基础知识的 go,Mocks 比传统的 state 测试验证更多的行为验证,在测试中,您在对它执行操作后检查组件的 state(Arrange,Act,Assert with Mocks 它更多 Arrange,Act,Verify):

State verification Assert.AreEqual(valueExpected,mycomponent.Property); State验证Assert.AreEqual(valueExpected,mycomponent.Property); Behavioral verification: myMock.WasCalled(MyMethod);行为验证:myMock.WasCalled(MyMethod);

Great Realtime Example of Mocking by Bert F Bert F的 Mocking 的实时示例

Unit Testing单元测试

Imagine unit testing for this system:想象一下这个系统的单元测试:

cook <- waiter <- customer

Its generally easy to envision testing a low-level component like the cook :通常很容易设想测试像cook这样的低级组件:

cook <- test driver

The test driver simply orders different dishes and verifies the cook returns the correct dish for each order.测试司机只需点不同的菜,并验证厨师为每个订单返回正确的菜。

Its harder to test a middle component, like the waiter, that utilizes the behavior of other components.更难测试中间组件,比如服务员,它利用了其他组件的行为。 A naive tester might test the waiter component the same way we tested the cook component:天真的测试人员可能会像测试 cook 组件一样测试 waiter 组件:

cook <- waiter <- test driver

The test driver would order different dishes and ensure the waiter returns the correct dish.测试司机会点不同的菜,并确保服务员返回正确的菜。 Unfortunately, that means that this test of the waiter component may be dependent on the correct behavior of the cook component.不幸的是,这意味着对服务员组件的测试可能依赖于厨师组件的正确行为。 This dependency is even worse if the cook component has any test-unfriendly characteristics, like non-deterministic behavior (the menu includes chef's surprise as an dish), lots of dependencies (cook won't cook without his entire staff), or lot of resources (some dishes require expensive ingredients or take an hour to cook).如果 cook 组件有任何测试不友好的特征,这种依赖性会更糟,比如不确定的行为(菜单包括厨师的惊喜作为一道菜),很多依赖性(厨师没有他的全体员工就不会做饭),或者很多资源(有些菜肴需要昂贵的原料或需要一个小时来烹饪)。

Since this a waiter test, ideally, we want to test just the waiter, not the cook.由于这是服务员测试,理想情况下,我们只想测试服务员,而不是厨师。 Specifically, we want to make sure the waiter conveys the customer's order to the cook correctly and delivers the cook's food to the customer correctly.具体来说,我们要确保服务员正确地将客户的订单传达给厨师,并将厨师的食物正确地送到客户手中。

Unit testing means testing units independently, so a better approach would be to isolate the component under test (the waiter) using what Fowler calls test doubles (dummies, stubs, fakes, mocks) .单元测试意味着独立测试单元,因此更好的方法是使用Fowler 所说的测试替身(假人、存根、假货、模拟)来隔离被测组件(服务员)

    -----------------------
   |                       |
   v                       |
test cook <- waiter <- test driver

Here, the test cook is "in cahoots" with the test driver.在这里,测试厨师与测试驾驶员“勾结”。 Ideally, the system under test is designed so that the test cook can be easily substituted ( injected ) to work with the waiter without changing production code (eg without changing the waiter code).理想情况下,被测系统被设计成可以很容易地替代(注入)测试厨师与服务员一起工作,而无需更改生产代码(例如,无需更改服务员代码)。

Mock Objects模拟对象

Now, the test cook (test double) could be implemented different ways:现在,测试厨师(测试替身)可以用不同的方式实现:

  • a fake cook - a someone pretending to be a cook by using frozen dinners and a microwave,假厨师 - 使用冷冻食品和微波炉冒充厨师的人,
  • a stub cook - a hot dog vendor that always gives you hot dogs no matter what you order, or一个小厨师——无论你点什么,总是给你热狗的热狗供应商,或者
  • a mock cook - an undercover cop following a script pretending to be a cook in a sting operation.模拟厨师 - 一名卧底警察按照剧本假装是一名厨师进行诱捕行动。

See Fowler's article for the more specifics about fakes vs stubs vs mocks vs dummies , but for now, let's focus on a mock cook.请参阅Fowler 的文章,了解有关假货、存根、模拟物和假人的更多细节,但现在,让我们关注模拟厨师。

    -----------------------
   |                       |
   v                       |
mock cook <- waiter <- test driver

A big part of unit testing the waiter component focuses on how the waiter interacts with the cook component.单元测试 Waiter 组件的很大一部分集中在 Waiter 如何与 Cook 组件交互。 A mock-based approach focuses on fully specifying what the correct interaction is and detecting when it goes awry.基于模拟的方法侧重于完全指定正确的交互是什么,并检测它何时出错。

The mock object knows in advance what is supposed to happen during the test (eg which of its methods calls will be invoked, etc.) and the mock object knows how it is supposed to react (eg what return value to provide).模拟 object 预先知道在测试期间应该发生什么(例如,它的哪些方法调用将被调用等)并且模拟 object 知道它应该如何反应(例如提供什么返回值)。 The mock will indicate whether what really happens differs from what is supposed to happen.模拟将指示实际发生的情况是否与应该发生的情况不同。 A custom mock object could be coded for the expected behavior of each test case, but a mocking framework strives to allow such a behavior specification to be clearly and easily indicated directly in the test case.自定义模拟 object 可以针对每个测试用例的预期行为进行编码,但 mocking 框架力求让此类行为规范能够在测试用例中直接明确且轻松地指示。

The conversation surrounding a mock-based test might look like this:围绕基于模拟的测试的对话可能如下所示:

test driver to mock cook : expect a hot dog order and give him this dummy hot dog in response模拟厨师试车手期待热狗订单并给他这个虚拟热狗作为回应

test driver (posing as customer) to waiter : I would like a hot dog please试驾(冒充顾客)对服务员我要热狗
waiter to mock cook : 1 hot dog please服务员模拟厨师请来一份热狗
mock cook to waiter : order up: 1 hot dog ready (gives dummy hot dog to waiter)服务员模拟厨师点单:准备好 1 个热狗(将虚拟热狗交给服务员)
waiter to test driver : here is your hot dog (gives dummy hot dog to test driver)服务员试驾员这是你的热狗(给试驾员假人热狗)

test driver : TEST SUCCEEDED!测试驱动程序:测试成功!

But since our waiter is new, this is what could happen:但由于我们的服务员是新来的,所以可能会发生以下情况:

test driver to mock cook : expect a hot dog order and give him this dummy hot dog in response模拟厨师试车手期待热狗订单并给他这个虚拟热狗作为回应

test driver (posing as customer) to waiter : I would like a hot dog please试驾(冒充顾客)对服务员我要热狗
waiter to mock cook : 1 hamburger please服务员模拟厨师请给我 1 个汉堡包
mock cook stops the test: I was told to expect a hot dog order!模拟厨师停止测试:有人告诉我要点热狗!

test driver notes the problem: TEST FAILED!测试驱动程序注意到问题:TEST FAILED! - the waiter changed the order - 服务员改变了订单

or要么

test driver to mock cook : expect a hot dog order and give him this dummy hot dog in response模拟厨师试车手期待热狗订单并给他这个虚拟热狗作为回应

test driver (posing as customer) to waiter : I would like a hot dog please试驾(冒充顾客)对服务员我要热狗
waiter to mock cook : 1 hot dog please服务员模拟厨师请来一份热狗
mock cook to waiter : order up: 1 hot dog ready (gives dummy hot dog to waiter)服务员模拟厨师点单:准备好 1 个热狗(将虚拟热狗交给服务员)
waiter to test driver : here is your french fries (gives french fries from some other order to test driver)服务员试驾这是你的炸薯条(把其他订单的炸薯条给试驾)

test driver notes the unexpected french fries: TEST FAILED!测试司机注意到意想不到的炸薯条:测试失败! the waiter gave back wrong dish服务员退错菜了

It may be hard to clearly see the difference between mock objects and stubs without a contrasting stub-based example to go with this, but this answer is way too long already:-)如果没有与 go 对比的基于存根的示例,可能很难清楚地看到模拟对象和存根之间的区别,但是这个答案已经太长了:-)

Also note that this is a pretty simplistic example and that mocking frameworks allow for some pretty sophisticated specifications of expected behavior from components to support comprehensive tests.另请注意,这是一个非常简单的示例,mocking 框架允许对组件的预期行为进行一些非常复杂的规范,以支持全面的测试。 There's plenty of material on mock objects and mocking frameworks for more information.有关模拟对象和 mocking 框架的大量资料可提供更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM