Java 模拟框架如何工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2993464/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do Java mocking frameworks work?
提问by Amir Rachum
This is NOT a question about which is the best framework, etc.
这不是关于哪个是最好的框架等的问题。
I have never used a mocking framework and I'm a bit puzzled by the idea. How does it know how to create the mock object? Is it done at runtime or does it generate a file? How do you know its behavior? And most importantly – what is the workflow of using such a framework (what is the step-by-step for creating a test)?
我从来没有使用过模拟框架,我对这个想法有点困惑。它如何知道如何创建模拟对象?它是在运行时完成还是生成文件?你怎么知道它的行为?最重要的是 - 使用这样一个框架的工作流程是什么(创建测试的步骤是什么)?
Can anyone explain? You can choose whichever framework you like for example, just say what it is.
谁能解释一下?例如,您可以选择您喜欢的任何框架,只需说出它是什么。
采纳答案by Alexander Pogrebnyak
I will speak of the framework that I use ( jmock ), but others do something very similar.
我将谈论我使用的框架 ( jmock ),但其他人做了一些非常相似的事情。
How does it know how to create the mock object?
它如何知道如何创建模拟对象?
It does not, you tell the framework that you require a mock object and you give it a type ( ideally an interface ) of the object that you would like to mock. Behind the scenes the mock framework uses a combination of reflection and, sometimes, byte-code rewrites to create a mock object.
它不是,你告诉框架你需要一个模拟对象,然后你给它一个你想要模拟的对象的类型(最好是一个接口)。在幕后,模拟框架使用反射的组合,有时还使用字节码重写来创建模拟对象。
Is it done in runtime or generates a file?
它是在运行时完成还是生成一个文件?
jMock creates it at runtime.
jMock 在运行时创建它。
How do you know its behavior?
你怎么知道它的行为?
Mock objects are pretty dumb. You specify the behavior that you expect of them.
模拟对象非常愚蠢。您指定您期望它们的行为。
And most importantly - what is the work flow of using such a framework (what is the step-by-step for creating a test).
最重要的是 - 使用这种框架的工作流程是什么(创建测试的分步是什么)。
One very important thing that framework must provide is an ability to check that expected behavior has been observed during the test execution.
框架必须提供的一项非常重要的功能是检查在测试执行期间是否观察到预期行为。
jmock does it by introducing specific test runner that checks that all expectations on all declared mock objects after the test has finished. If something does not match, the exception is thrown.
jmock 通过引入特定的测试运行程序来完成它,该测试运行程序在测试完成后检查所有声明的模拟对象的所有期望。如果某些内容不匹配,则抛出异常。
Usually the pattern is:
通常的模式是:
- Acquire mock factory ( called Mockery in jmock )out of thin air ( with specific no-args constructor ).
- Create required mock objects.
- Setup expectation on mock objects
- Call the method you are testing, passing mocks instead of real objects
- If your method returns some values, check expected value with regular
assertXXXmethods.
- 凭空获取模拟工厂(在 jmock 中称为 Mockery)(使用特定的无参数构造函数)。
- 创建所需的模拟对象。
- 设置对模拟对象的期望
- 调用您正在测试的方法,传递模拟而不是真实对象
- 如果您的方法返回一些值,请使用常规
assertXXX方法检查预期值。
回答by Yishai
A mocking framework takes the redundancy and boilerplate out of a mocking test.
模拟框架从模拟测试中去除了冗余和样板。
It knows to create the Mock object because you tell it to, of course (unless you meant something else with that question).
它知道创建 Mock 对象,因为你告诉它,当然(除非你对这个问题有别的意思)。
The "standard" way of creating a mock object is to use/abuse the java.lang.reflect.Proxy class to create a runtime implementation of the interface. This is done at runtime. Proxy has a limitation in that it cannot proxy concrete classes. To accomplish mocking of concrete classes requires dynamic bytecode creation that creates subclasses that override the real implementation of the public methods with essentially what would be done with a Proxy (record the method parameters and return a pre-determined value). This has a limitation in that it cannot subclass final classes. For that, you have solutions like JDave, which (I believe, I didn't confirm this) muck with the classloader to remove the final designation on the class before loading it, so the class isn't in fact final at runtime as far as the JVM is concerned.
创建模拟对象的“标准”方法是使用/滥用 java.lang.reflect.Proxy 类来创建接口的运行时实现。这是在运行时完成的。代理有一个限制,它不能代理具体的类。要完成对具体类的模拟,需要创建动态字节码,以创建子类来覆盖公共方法的实际实现,本质上是使用代理来完成的(记录方法参数并返回一个预先确定的值)。这有一个限制,因为它不能对 final 类进行子类化。为此,您有像JDave这样的解决方案,它(我相信,我没有确认这一点)与类加载器混淆,以在加载之前删除类的最终名称,因此就 JVM 而言,该类实际上在运行时并不是最终的。
The Mocking framework is basically all about capturing the parameters and verifying them against pre-determined expectations, and then returning a pre-configured or reasonable default value. It doesn't behave in any particular way, which is the point. The calling code is being verified that it calls the method with the correct parameter, and perhaps how it reacts to a specific return values or thrown exceptions. Any side effects or real accomplishments of the call on the real object do not happen.
Mocking 框架基本上都是关于捕获参数并根据预先确定的期望验证它们,然后返回预先配置或合理的默认值。它没有任何特定的行为,这就是重点。正在验证调用代码是否使用正确的参数调用方法,以及它如何对特定的返回值或抛出的异常做出反应。对真实对象调用的任何副作用或实际成就都不会发生。
Here is a real example from a project, using JMock with JUnit4. I have added comments to explain what is going on.
这是一个来自项目的真实示例,使用 JMock 和 JUnit4。我添加了评论来解释正在发生的事情。
@RunWith(JMock.class) //The JMock Runner automatically checks that the expectations of the mock were actually run at the end of the test so that you don't have to do it with a line of code in every test.
public class SecuredPresentationMapTest {
private Mockery context = new JUnit4Mockery(); //A Mockery holds the state about all of the Mocks. The JUnit4Mockery ensures that a failed mock throws the same Error as any other JUnit failure.
@Test
public void testBasicMap() {
final IPermissionsLookup lookup = context.mock(IPermissionsLookup.class); //Creating a mock for the interface IPermissionsLookup.
context.checking(new Expectations(){{ //JMock uses some innovative but weird double brace initialization as its standard idom.
oneOf(lookup).canRead(SecuredEntity.ACCOUNTING_CONTRACT);
//expect exactly one call to the IPermissionsLookup.canRead method with the the enum of ACCOUNTING_CONTRACT as the value. Failure to call the method at all causes the test to fail.
will(returnValue(true)); //when the previous method is called correctly, return true;
}});
Map<String, Component> map = new SecuredPresentationMap(lookup, SecuredEntity.ACCOUNTING_CONTRACT);
//This creates the real object under test, but passes a mock lookup rather than the real implementation.
JLabel value = new JLabel();
map.put("", value);
assertThat(((JLabel) map.get("")), is(value)); //This ensures that the Map returns the value placed, which it should based on the fact that the mock returned true to the security check.
}
}
If the mock passed in was ignored, the test would have failed. If the map fails to return the value placed in it the test fails (that is standard JUnit).
如果忽略传入的模拟,则测试将失败。如果映射未能返回放置在其中的值,则测试失败(即标准 JUnit)。
What is being tested here and in another opposite test is that depending on what the IPermissionsLookup interface says about the security the Map changes its behavior about what is returned. This is the base good case. The other test, the mock returns false and something else is expected from the Map. Using the mock ensures that the map relies on the IPermissionsLookup method to determine the security posture and what is being returned.
这里和另一个相反的测试中正在测试的是,根据 IPermissionsLookup 接口对安全性的说明,Map 会更改其关于返回内容的行为。这是基本的好案例。在另一个测试中,模拟返回 false 并且从 Map 中期望得到其他信息。使用模拟可确保映射依赖于 IPermissionsLookup 方法来确定安全状态和返回的内容。
回答by Jonny Cundall
For a better understanding of mocking, you might like to make your own mock object. It's a pretty simple process - you make a class which implement the same interface as what you're mocking, and give it the behaviour you require, either recording calls to a method, or responding in a set way when called. From there the way that mocking frameworks are set up begins to make more sense.
为了更好地理解模拟,您可能想要制作自己的模拟对象。这是一个非常简单的过程 - 您创建一个实现与您模拟的相同接口的类,并为其提供所需的行为,或者记录对方法的调用,或者在调用时以设置的方式响应。从那里,模拟框架的设置方式开始变得更有意义。
回答by Bozhidar Batsov
I think that EasyMock's documentationis a get way to get you started. It features a lot of examples that are easy to grasp.
我认为EasyMock 的文档是让您入门的一种入门方式。它具有许多易于掌握的示例。
回答by dplass
EasyMock (http://easymock.org/) is the mocking library that I have used the most. (There are others: jMock, Mockito, etc.)
EasyMock ( http://easymock.org/) 是我使用最多的模拟库。(还有其他的:jMock、Mockito 等)
Most of these create an object at runtime, that you can control. The basic flow is:
其中大多数在运行时创建一个您可以控制的对象。基本流程是:
- Create a mock
- Specify how it should act by telling the mock framework what calls to expect. (This varies based on the framework.)
- Use the mock in your test as if it were the real thing
- Some frameworks allow (or require) that you "verify" that the mock was used correctly.
- 创建一个模拟
- 通过告诉模拟框架什么叫期望来指定它应该如何行动。(这因框架而异。)
- 在测试中使用模拟,就好像它是真实的一样
- 某些框架允许(或要求)您“验证”是否正确使用了模拟。

