java 使用 JMockit 模拟自动装配的接口实现
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1980596/
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
Using JMockit to mock autowired interface implementations
提问by SwimsZoots
We are writing JUnit tests for a class that uses Spring autowiring to inject a dependency which is some instance of an interface. Since the class under test never explicitly instantiates the dependency or has it passed in a constructor, it appears that JMockit doesn't feel obligated to instantiate it either.
我们正在为一个类编写 JUnit 测试,该类使用 Spring 自动装配来注入依赖项,该依赖项是接口的某个实例。由于被测类从未显式实例化依赖项或将其传递给构造函数,因此 JMockit 似乎也没有义务实例化它。
Up until now we have been using SpringRunner to have Spring load mock dependencies for us, which works. Two things we don't like about this are 1) the Spring framework has to be loaded and initialized each time running the tests which is not exactly speedy, and 2) we are forced to explicitly create all mock dependencies as real classes, something which JMockit helps eliminate.
到目前为止,我们一直在使用 SpringRunner 为我们加载模拟依赖项,这很有效。我们不喜欢的两件事是 1) 每次运行测试时都必须加载和初始化 Spring 框架,这并不完全是快速的,以及 2) 我们被迫将所有模拟依赖项显式创建为真正的类,这是JMockit 有助于消除。
Here's a simplified example of what we're testing:
以下是我们正在测试的内容的简化示例:
public class UnitUnderTest {
@Autowired
ISomeInterface someInterface;
public void callInterfaceMethod() {
System.out.println( "UnitUnderTest.callInterfaceMethod calling someInterface.doSomething");
someInterface.doSomething();
}
}
So, the question is, is there a way to have JMockit create a mock someInterface?
所以,问题是,有没有办法让 JMockit 创建一个模拟someInterface?
回答by Rogério
JMockit will always instantiate a mocked interface (except in the case of a final mock field), but that only occurs in test code. It will not automatically inject the instance into code under test.
JMockit 将始终实例化模拟接口(最终模拟字段的情况除外),但这仅发生在测试代码中。它不会自动将实例注入到被测代码中。
You would have to manually inject the mock instance. For example:
您必须手动注入模拟实例。例如:
public class SomeTest
{
@Autowired UnitUnderTest unitUnderTest;
@Mocked ISomeInterface theMock; // created and assigned automatically
@Test
public void testSomeMethod()
{
Deencapsulation.setField(unitUnderTest, theMock);
//proceed with unit test here
}
}
mockit.Deencapsulationis a Reflection-based utility class that lets you invoke private methods, get/set fields, etc.
mockit.Deencapsulation是一个基于反射的实用程序类,可让您调用私有方法、获取/设置字段等。
回答by Paul McKenzie
You can use org.springframework.test.util.ReflectionTestUtilsto explicitly inject your mocked ISomeInterfacein your test case.
您可以使用在测试用例中org.springframework.test.util.ReflectionTestUtils显式注入您的模拟ISomeInterface。
See documentation
查看文档
回答by SwimsZoots
With the hints kindly provided above, here's what I found most useful as someone pretty new to JMockit: JMockit provides the Deencapsulationclass to allow you to set the values of private dependent fields (no need to drag the Spring libraries in), and the MockUpclass that allows you to explicitly create an implementation of an interface and mock one or more methods of the interface. Here's how I ended up solving this particular case:
通过上面友情提供的线索,这里是我发现有人很新的JMockit最有用:JMockit提供了Deencapsulation类,允许您设置私有依赖字段的值(无需拖动Spring库),以及MockUp类允许您显式创建接口的实现并模拟接口的一个或多个方法。这是我最终解决这个特殊情况的方法:
@Before
public void setUp() {
IMarketMakerDal theMock = new MockUp <IMarketMakerDal>() {
@Mock
MarketMakerDcl findByMarketMakerGuid( String marketMakerGuid ) {
MarketMakerDcl marketMakerDcl = new MarketMakerDcl();
marketMakerDcl.setBaseCurrencyCode( CURRENCY_CODE_US_DOLLAR );
return marketMakerDcl;
}
}.getMockInstance();
setField( unitUnderTest, theMock );
}
Thanks everyone for the help.
感谢大家的帮助。
回答by simomo
For those people who met
对于那些遇见的人
java.lang.IllegalStateException: Missing @Injectable for field ***
or
或者
java.lang.IllegalStateException: Missing @Tested class for field ***
error when using jmockitto mock @autowiredfield in spring( or spring boot) framework, I did below two steps to avoid above errors:
jmockit用于模拟( 或) 框架中的@autowired字段时出错,我执行了以下两个步骤以避免上述错误:springspring boot
- use
@Tested(fullyInitialized=true)instead of@Tested
- 使用
@Tested(fullyInitialized=true)代替@Tested
https://groups.google.com/forum/#!msg/jmockit-users/uo0S51lSX24/lQhLNN--eJcJ
https://groups.google.com/forum/#!msg/jmockit-users/uo0S51lSX24/lQhLNN--eJcJ
- revert jmockit's version back to
1.18or previous ones
- 将 jmockit 的版本恢复到
1.18以前的版本
https://groups.google.com/forum/#!topic/jmockit-users/wMFZggsA8LM
https://groups.google.com/forum/#!topic/jmockit-users/wMFZggsA8LM
回答by Denis Makarskiy
If you have a @Qualifier annotation for the interface, you need to name your @Injectable field exactly as it is named in qualifier.
如果您有接口的@Qualifier 注释,则需要完全按照限定符中的名称命名@Injectable 字段。
Here is quote from JMockit doc:
这是JMockit 文档的引述:
Custom names specified in field annotations from Java EE (@Resource(name), @Named) or the Spring framework (@Qualifier) are used when looking for a matching @Injectable or @Tested value. When such a name contains a - (dash) or . (dot) character, the corresponding camel-cased name is used instead.
在查找匹配的 @Injectable 或 @Tested 值时,会使用在来自 Java EE(@Resource(name)、@Named)或 Spring 框架 (@Qualifier) 的字段注释中指定的自定义名称。当此类名称包含 -(破折号)或 . (点)字符,则使用相应的驼峰式名称。
For example:
例如:
@Component
public class AClass {
@Autowired
private Bean1 bean1;
@Autowired
@Qualifier("my-dashed-name")
private AmqpTemplate rpcTemplate;
}
Unit test class:
单元测试类:
public class AClassTest {
@Injectable
private Bean1 bean1;
@Injectable
private AmqpTemplate myDashedName;
@Tested
private AClass aClass = new AClass();
}
Also there is no need to use setFiled for each @Autowired bean, all fields injects automatically when @Tested class instantiated. Tested on JMockit ver. 1.30
也不需要为每个 @Autowired bean 使用 setFiled,当 @Tested 类实例化时,所有字段都会自动注入。在 JMockit 版本上测试。1.30

