什么是模拟,什么时候应该使用它?

时间:2021-03-20 14:34:59

I just read the Wikipedia article on mock objects, but I'm still not entirely clear on their purpose. It appears they are objects that are created by a test framework when the actual object would be too complex or unpredictable (you know 100% sure what the values of the mock object are because you fully control them).

我刚刚阅读了有关模拟对象的*文章,但我仍然不完全清楚它们的用途。当实际对象过于复杂或不可预测时,它们似乎是由测试框架创建的对象(您知道100%确定模拟对象的值是什么,因为您完全控制它们)。

However, I was under the impression that all testing is done with objects of known values, so I must be missing something. For example, in a course project, we were tasked with a calendar application. Our test suite consisted of event objects that we knew exactly what they were so we could test the interactions between multiple event objects, various subsystems, and the user interface. I'm guessing these are mock objects, but I don't know why you wouldn't do this because without the objects of known values, you can't test a system.

但是,我的印象是所有测试都是使用已知值的对象完成的,所以我必须遗漏一些东西。例如,在课程项目中,我们负责日历应用程序。我们的测试套件由事件对象组成,我们确切知道它们是什么,因此我们可以测试多个事件对象,各种子系统和用户界面之间的交互。我猜这些是模拟对象,但我不知道你为什么不这样做,因为没有已知值的对象,你无法测试系统。

5 个解决方案

#1


27  

A mock object is not just an object with known values. It is an object that has the same interface as a complex object that you cannot use in test (like a database connection and result sets), but with an implementation that you can control in your test.

模拟对象不仅仅是具有已知值的对象。它是一个与复杂对象具有相同接口的对象,您无法在测试中使用它(如数据库连接和结果集),但是您可以在测试中控制该实现。

There are mocking frameworks that allow you to create these objects on the fly and in essence allow you to say something like: Make me an object with a method foo that takes an int and returns a bool. When I pass 0, it should return true. Then you can test the code that uses foo(), to make sure it reacts appropriately.

有一些模拟框架允许你动态创建这些对象,实质上允许你说出这样的话:用一个方法foo让我成为一个对象,它接受一个int并返回一个bool。当我传递0时,它应该返回true。然后你可以测试使用foo()的代码,以确保它做出适当的反应。

Martin Fowler has a great article on mocking:

马丁福勒有一篇关于嘲笑的好文章:

#2


9  

Think of the classic case of having client and server software. To test the client, you need the server; to test the server, you need the client. This makes unit testing pretty much impossible - without using mocks. If you mock the server, you can test the client in isolation and vice versa.

想想拥有客户端和服务器软件的经典案例。要测试客户端,您需要服务器;要测试服务器,您需要客户端。这使得单元测试几乎不可能 - 不使用模拟。如果模拟服务器,则可以单独测试客户端,反之亦然。

The point of the mock is not to duplicate the behaviour of the things its mocking though. It is more to act as a simple state machine whose state changes can be analysed by the test framework. So a client mock might generate test data, send it to the server and then analyse the response. You expect a certain response to a specific request, and so you can test if you get it.

模拟的重点不是复制它嘲弄的东西的行为。它更像是一个简单的状态机,其状态变化可以通过测试框架进行分析。因此,客户端模拟可能会生成测试数据,将其发送到服务器,然后分析响应。您希望对特定请求做出某种响应,因此您可以测试是否得到了该请求。

#3


6  

I agree with everything @Lou Franco says and you should definitely read the excellent Martin Fowler article on test doubles that @Lou Franco points you to.

我同意@Lou Franco所说的一切,你一定要阅读@Lou Franco指出的关于测试双打的优秀Martin Fowler文章。

The main purpose of any test double (fake, stub or mock) is to isolate the object under test so that your unit test is only testing that object (not its dependencies and the other types it collaborates or interacts with).

任何测试double(假,存根或模拟)的主要目的是隔离被测对象,以便您的单元测试仅测试该对象(不是它的依赖关系以及它与之协作或交互的其他类型)。

An object that provides the interface that your object is dependent on can be used in place of the actual dependency so that expectations can be placed that certain interactions will occur. This can be useful but there is some controversy around state-based vs. interaction-based testing. Overuse of mock expectation will lead to brittle tests.

可以使用提供对象所依赖的接口的对象来代替实际的依赖关系,以便可以预期会发生某些交互。这可能很有用,但围绕基于状态和基于交互的测试存在一些争议。过度使用模拟期望将导致脆弱的测试。

A further reason for test doubles is to remove dependencies on databases or file systems or other types that are expensive to set up or perform time consuming operations. This means you can keep the time required to unit test the object you're interested in to a minimum.

测试双精度的另一个原因是删除对数据库或文件系统或设置或执行耗时操作昂贵的其他类型的依赖性。这意味着您可以将您感兴趣的对象单元测试所需的时间保持在最低限度。

#4


2  

Here's an example: if you're writing code that populates a database you may want to check if a particular method has added data to the database.

下面是一个示例:如果您正在编写填充数据库的代码,则可能需要检查特定方法是否已将数据添加到数据库。

Setting up a copy of the database for testing has the problem that if you assume there are no records before the call to the tested method and one record after, then you need to roll back the database to a previous state, thus adding to the overhead for running the test.

设置数据库的副本以进行测试存在的问题是,如果假设在调用测试方法之前没有记录,之后有一条记录,则需要将数据库回滚到先前的状态,从而增加了开销用于运行测试。

If you assume there is only one more record than before, it may * with a second tester (or even a second test in the same code) connecting to the same database, thus causing dependencies and making the tests fragile.

如果您假设只有一个记录比以前多一个,它可能会与连接到同一数据库的第二个测试人员(甚至是同一代码中的第二个测试人员)发生冲突,从而导致依赖性并使测试变得脆弱。

The mock allows you to keep the tests independent of each other and easy to set up.

模拟允许您使测试彼此独立并易于设置。

This is just one example - I'm sure others can supply more.

这只是一个例子 - 我相信其他人可以提供更多。

I agree 100% with the other contributors on this topic, especially with the recommendation for the Martin Fowler article.

我同意100%与其他贡献者就此主题达成一致,特别是对Martin Fowler文章的建议。

#5


0  

You might be interested in our book, see http://www.growing-object-oriented-software.com/. It's in Java, but the ideas still apply.

您可能对我们的书感兴趣,请访问http://www.growing-object-oriented-software.com/。它是用Java编写的,但这些想法仍然适用。

#1


27  

A mock object is not just an object with known values. It is an object that has the same interface as a complex object that you cannot use in test (like a database connection and result sets), but with an implementation that you can control in your test.

模拟对象不仅仅是具有已知值的对象。它是一个与复杂对象具有相同接口的对象,您无法在测试中使用它(如数据库连接和结果集),但是您可以在测试中控制该实现。

There are mocking frameworks that allow you to create these objects on the fly and in essence allow you to say something like: Make me an object with a method foo that takes an int and returns a bool. When I pass 0, it should return true. Then you can test the code that uses foo(), to make sure it reacts appropriately.

有一些模拟框架允许你动态创建这些对象,实质上允许你说出这样的话:用一个方法foo让我成为一个对象,它接受一个int并返回一个bool。当我传递0时,它应该返回true。然后你可以测试使用foo()的代码,以确保它做出适当的反应。

Martin Fowler has a great article on mocking:

马丁福勒有一篇关于嘲笑的好文章:

#2


9  

Think of the classic case of having client and server software. To test the client, you need the server; to test the server, you need the client. This makes unit testing pretty much impossible - without using mocks. If you mock the server, you can test the client in isolation and vice versa.

想想拥有客户端和服务器软件的经典案例。要测试客户端,您需要服务器;要测试服务器,您需要客户端。这使得单元测试几乎不可能 - 不使用模拟。如果模拟服务器,则可以单独测试客户端,反之亦然。

The point of the mock is not to duplicate the behaviour of the things its mocking though. It is more to act as a simple state machine whose state changes can be analysed by the test framework. So a client mock might generate test data, send it to the server and then analyse the response. You expect a certain response to a specific request, and so you can test if you get it.

模拟的重点不是复制它嘲弄的东西的行为。它更像是一个简单的状态机,其状态变化可以通过测试框架进行分析。因此,客户端模拟可能会生成测试数据,将其发送到服务器,然后分析响应。您希望对特定请求做出某种响应,因此您可以测试是否得到了该请求。

#3


6  

I agree with everything @Lou Franco says and you should definitely read the excellent Martin Fowler article on test doubles that @Lou Franco points you to.

我同意@Lou Franco所说的一切,你一定要阅读@Lou Franco指出的关于测试双打的优秀Martin Fowler文章。

The main purpose of any test double (fake, stub or mock) is to isolate the object under test so that your unit test is only testing that object (not its dependencies and the other types it collaborates or interacts with).

任何测试double(假,存根或模拟)的主要目的是隔离被测对象,以便您的单元测试仅测试该对象(不是它的依赖关系以及它与之协作或交互的其他类型)。

An object that provides the interface that your object is dependent on can be used in place of the actual dependency so that expectations can be placed that certain interactions will occur. This can be useful but there is some controversy around state-based vs. interaction-based testing. Overuse of mock expectation will lead to brittle tests.

可以使用提供对象所依赖的接口的对象来代替实际的依赖关系,以便可以预期会发生某些交互。这可能很有用,但围绕基于状态和基于交互的测试存在一些争议。过度使用模拟期望将导致脆弱的测试。

A further reason for test doubles is to remove dependencies on databases or file systems or other types that are expensive to set up or perform time consuming operations. This means you can keep the time required to unit test the object you're interested in to a minimum.

测试双精度的另一个原因是删除对数据库或文件系统或设置或执行耗时操作昂贵的其他类型的依赖性。这意味着您可以将您感兴趣的对象单元测试所需的时间保持在最低限度。

#4


2  

Here's an example: if you're writing code that populates a database you may want to check if a particular method has added data to the database.

下面是一个示例:如果您正在编写填充数据库的代码,则可能需要检查特定方法是否已将数据添加到数据库。

Setting up a copy of the database for testing has the problem that if you assume there are no records before the call to the tested method and one record after, then you need to roll back the database to a previous state, thus adding to the overhead for running the test.

设置数据库的副本以进行测试存在的问题是,如果假设在调用测试方法之前没有记录,之后有一条记录,则需要将数据库回滚到先前的状态,从而增加了开销用于运行测试。

If you assume there is only one more record than before, it may * with a second tester (or even a second test in the same code) connecting to the same database, thus causing dependencies and making the tests fragile.

如果您假设只有一个记录比以前多一个,它可能会与连接到同一数据库的第二个测试人员(甚至是同一代码中的第二个测试人员)发生冲突,从而导致依赖性并使测试变得脆弱。

The mock allows you to keep the tests independent of each other and easy to set up.

模拟允许您使测试彼此独立并易于设置。

This is just one example - I'm sure others can supply more.

这只是一个例子 - 我相信其他人可以提供更多。

I agree 100% with the other contributors on this topic, especially with the recommendation for the Martin Fowler article.

我同意100%与其他贡献者就此主题达成一致,特别是对Martin Fowler文章的建议。

#5


0  

You might be interested in our book, see http://www.growing-object-oriented-software.com/. It's in Java, but the ideas still apply.

您可能对我们的书感兴趣,请访问http://www.growing-object-oriented-software.com/。它是用Java编写的,但这些想法仍然适用。