我想测试一个以"ObjectMapper"作为实例变量的类:
public class EventTransformerImpl implements EventTransformer {
@Inject
private ObjectMapper objectMapper;
private String convertParametersToJson(NotificationConfig config) {
String parametersAsJson = null;
try {
parametersAsJson = objectMapper.writeValueAsString(config.getOrdinaryParameters());
} catch (IOException e) {
logger.info("There was a problem in writing json:" + config.getParameters(), e);
parametersAsJson = null;
}
return parametersAsJson;
}
}
这个类没有任何用于初始化"objectMapper"的"setter"或"constructor"(它是使用"spring"初始化的):
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd'T'HH:mm:ssZ"/>
</bean>
</property>
</bean>
当我想测试"EventTransformerImpl"类时,objectMapper的值为null,我如何在单元测试类中将"objectMapper"传递给"EventTransformerCimpl"。
这是一个典型的例子,其中mock框架(如Mockito)可以帮助您:
public class TestClass {
@Mock
private ObjectMapper objectMapper;
@InjectMocks
private EventTransformer eventTransformer;
@BeforeMethod
public void setUp() {
eventTransformer = new EventTransformerImpl();
MockitoAnnotations.initMocks(this);
}
}
这里,@Mock
和@InjectMocks
是Mockito框架的一部分。魔术发生在MockitoAnnotations.initMocks(this)
中,它将扫描this
,在本例中为TestClass
,以获取这些注释。Mockito将objectMapper
初始化为mock,并将其注入到eventTransformer
中。然后,您可以使用Mockito来决定objectMapper
的行为方式。
你可以在这里阅读更多关于Mockito的信息。
此外,@BeforeMethod
是一种TestNG方法,类似于JUnits@Before
。
然而,许多人更喜欢构造函数注入,正如davidxx所提出的那样。这将使EventTransformerImpl
具有哪些依赖关系更加清晰,并强制使用正确的依赖关系对其进行初始化。通过这样做,就不需要"神奇地"(我猜Mockito在后台使用反射)注入依赖项,只需通过调用构造函数来初始化要测试的类。
您的类可能看起来像这样(这里使用Spring的@Autowired
注释):
public class EventTransformerImpl implements EventTransformer {
private ObjectMapper objectMapper;
@Autowired
public EventTransformerImpl(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
private String convertParametersToJson(NotificationConfig config) {
String parametersAsJson = null;
try {
parametersAsJson = objectMapper.writeValueAsString(config.getOrdinaryParameters());
} catch (IOException e) {
logger.info("There was a problem in writing json:" + config.getParameters(), e);
parametersAsJson = null;
}
return parametersAsJson;
}
}
这将不太容易出错,因为使用@InjectMocks
,如果注入失败,Mockito将静默地忽略注入,而调用构造函数将使测试类完全控制测试的初始化。
为了在JUnit测试和它们所依赖的其他对象中注入Spring依赖项,我使用
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:testBeans.xml" })
public class TestRunner {
@Test
public void test1() {
您可以提供代码内bean初始化以及
@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
更多信息,请访问https://www.mkyong.com/unittest/junit-spring-integration-example/
这三个答案都很好;所以我不需要重复这些细节,但我认为在"完整"的画面上"对齐"是值得的。
如图所示,Spring具有为单元测试进行注入的内置方法;你可以继续使用它。但当然,这意味着您将业务逻辑代码更多地"耦合"到Spring框架。
因此,如果您曾经考虑在非Spring的情况下重新使用您的代码,那么您会发现不仅您的生产代码,而且您的单元测试都需要大量返工才能在没有Spring的情况中使用。
因此,建议更改生产代码的另外两个答案有一定的优点,使您能够在不添加对Spring的依赖的情况下至少完全测试生产代码。
1)类应该是自然可测试的:为什么不在EventTransformerImpl
中添加一个构造函数来注入映射程序呢
你可以在春季配置中做类似的事情:
<bean id="EventTransformer" class="EventTransformerImpl">
<constructor-arg ref="objectMapper"/>
</bean>
在测试中,您可以在构造函数中使用ObjectMapper
模拟依赖关系实例化EventTransformerImpl
。
2) 另一种解决方案是使用反射在测试方法中的被测类中注入字段
您可以使用Spring的ReflectionTestUtils
类的setField()
方法来设置依赖关系,或者如果您不使用Spring,您可以编写一个简单的实用程序方法来完成这项工作:
public static final void injectValue(String fieldName, Object fieldValue, Object targetObject) {
try {
Field f = targetObject.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(targetObject, fieldValue);
} catch (Exception e) {
// handle the exception
}
}