这不是关于哪个框架最好之类的问题。
我从未使用过模拟框架,对于这个概念感到有些困惑。它是如何知道如何创建模拟对象的?是在运行时完成还是生成一个文件?你怎么知道它的行为?最重要的是 - 使用这样的框架的工作流程是什么(创建测试的逐步过程是什么)?
有人能解释一下吗?您可以选择任何您喜欢的框架,只需要说明是什么框架即可。
这不是关于哪个框架最好之类的问题。
我从未使用过模拟框架,对于这个概念感到有些困惑。它是如何知道如何创建模拟对象的?是在运行时完成还是生成一个文件?你怎么知道它的行为?最重要的是 - 使用这样的框架的工作流程是什么(创建测试的逐步过程是什么)?
有人能解释一下吗?您可以选择任何您喜欢的框架,只需要说明是什么框架即可。
一个模拟框架可以减少模拟测试中的冗余和样板代码。
当然,它知道要创建模拟对象是因为你告诉它,除非你在提问时指的是其他东西。
创建模拟对象的“标准”方法是使用/滥用java.lang.reflect.Proxy类创建接口的运行时实现。这是在运行时完成的。Proxy有一个限制,即它无法代理具体类。要完成对具体类的模拟,需要动态生成字节码来创建覆盖公共方法真实实现的子类,与Proxy(记录方法参数并返回预先确定的值)实际上是相同的。这有一个限制,即它不能为final类创建子类。为此,您有像JDave这样的解决方案,它(我相信我没有确认这一点)在加载之前与类加载器混淆以移除类的最终指定,因此就JVM而言,该类在运行时实际上不是final的。
模拟框架基本上都是关于捕获参数并根据预先确定的期望进行验证,然后返回预配置或合理的默认值。它不会以任何特定的方式行事,这就是重点。调用代码正在验证其是否以正确的参数调用了方法,以及它如何响应特定的返回值或抛出异常。对真实对象的任何副作用或真正成就都不会发生。
这是来自一个项目的实际示例,使用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.
}
}
我将谈论我使用的框架 ( jmock ),但其他框架做类似的事情。
它如何知道如何创建模拟对象?
它不知道,你告诉框架你需要一个模拟对象,并给它提供对象类型(最好是接口)。 在幕后,模拟框架使用反射和有时候是字节码重写的组合来创建模拟对象。
这是在运行时完成还是生成文件?
jMock 在运行时创建它。
你怎么知道它的行为呢?
模拟对象非常笨。你指定期望的行为。
最重要的是 - 使用这样的框架的工作流是什么 (创建测试的逐步操作是什么)?
框架必须提供的一个非常重要的功能是检查在测试执行过程中是否观察到了期望的行为。
jmock通过引入特定的测试运行程序来实现此功能。该程序在测试结束后检查所有声明的模拟对象上的所有期望。如果有不匹配的地方,则会抛出异常。
通常,步骤如下:
assertXXX
方法检查期望值。为了更好地理解mocking,您可能希望制作自己的mock对象。这是一个相当简单的过程 - 您可以创建一个实现与您要mock的接口相同的类,并赋予它所需的行为,无论是记录对方法的调用还是在调用时以一种设置方式响应。从那里开始,mocking框架的设置方式就开始变得更加清晰了。
EasyMock(http://easymock.org/)是我使用最多的模拟库之一。(还有其他的:jMock,Mockito等)
这些库大多数在运行时创建一个对象,您可以控制它。基本流程如下: