今天有点空了,把单元测试的那点事整理了一下。个人觉得Spring应用和Mockito结合做单元测试简单实用,分享出来供参考。
这里不啰嗦单元测试的重要性...。很多应用是基于Spring,而Mockito简单易用易上手,所以就把Spring和Mockito组合做单元测试,Mocked对象也交给Spring统一管理。好处至少有:使单元测试类的环境和应用实际的环境保持一致性。开发人员就不用另外增加额外的配置,也可以少一些代码。单元测试类通过了,相应的应用类也就ok(Spring的相关配置也ok)。
1,为了把Mock对象也纳入Spring。需继承DependencyInjectionTestExecutionListener并增加Mock对象和注入依赖对象为Mock对象。
代码:
public class MockitoDependencyInjectionTestExecutionListener extends DependencyInjectionTestExecutionListener {
private static final Map<String, MockObject> mockObject = new HashMap<String, MockObject>();
private static final List<Field> injectFields = new ArrayList<Field>();
@Override
protected void injectDependencies(final TestContext testContext) throws Exception {
super.injectDependencies(testContext);
init(testContext);
}
protected void injectMock(final TestContext testContext) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext().getAutowireCapableBeanFactory();
for (Field field : injectFields) {
Object o = beanFactory.getBean(field.getName(), field.getType());
if (null != o) {
Method[] methods = o.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.getName().startsWith("set")) {
for (Iterator it = mockObject.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
if (method.getName().equalsIgnoreCase("set" + key)) {
method.invoke(o, mockObject.get(key).getObj());
break;
}
}
}
}
}
}
}
private void init(final TestContext testContext) throws Exception {
Object bean = testContext.getTestInstance();
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getAnnotations();
for (Annotation antt : annotations) {
if (antt instanceof org.mockito.Mock) {
// 注入mock实例
MockObject obj = new MockObject();
obj.setType(field.getType());
obj.setObj(mock(field.getType()));
field.setAccessible(true);
field.set(bean, obj.getObj());
mockObject.put(field.getName(), obj);
} else if (antt instanceof Autowired) {
// 只对autowire重新注入
injectFields.add(field);
}
}
}
for (Field field : injectFields) {
field.setAccessible(true);
Object object = field.get(bean);
if (object instanceof Proxy) {
// 如果是代理的话,找到真正的对象
Class targetClass = AopUtils.getTargetClass(object);
if (targetClass == null) {
// 可能是远程实现
return;
}
Field[] targetFields = targetClass.getDeclaredFields();
for (int i = 0; i < targetFields.length; i++) {
// 针对每个需要重新注入的字段
for (Map.Entry<String, MockObject> entry : mockObject.entrySet()) {
// 针对每个mock的字段
if (targetFields[i].getName().equals(entry.getKey())) {
targetFields[i].setAccessible(true);
targetFields[i].set(getTargetObject(object, entry.getValue().getType()),
entry.getValue().getObj());
}
}
}
} else {
injectMock(testContext);
}
}
}
protected <T> T getTargetObject(Object proxy, Class<T> targetClass) throws Exception {
if (AopUtils.isJdkDynamicProxy(proxy)) {
return (T) ((Advised) proxy).getTargetSource().getTarget();
} else {
return (T) proxy; // expected to be cglib proxy then, which is simply a specialized class
}
}
public static class MockObject {
private Object obj;
private Class<?> type;
public MockObject(){
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
}
public static Map<String, MockObject> getMockobject() {
return mockObject;
}
public static List<Field> getInjectfields() {
return injectFields;
}
}
2,单元测试继承AbstractJUnit4SpringContextTests类。这里用一个抽象类把必须的注解上。
代码:
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({ MockitoDependencyInjectionTestExecutionListener.class })
public abstract class BaseTestCase extends AbstractJUnit4SpringContextTests {
}
3,测试用例 demo。注意在MockitoDependencyInjectionTestExecutionListener中已经mock对象了,不要再用MockitoAnnotations.initMocks(this)。
代码:
@ContextConfiguration(locations = { "/applicationContext.xml" })
public class DemoServiceTest extends BaseTestCase {
@Autowired
private DemoService demoService;
@Mock
private DemoDao demoDao;
@Test
public void testGetResults() {
List<Model> list = new ArrayList<Model>();
Model model = new Model("id", "name");
list.add(model);
// 先设置预期
when(demoDao.getModel("id")).thenReturn(model);
// when(demoDao.getResults()).thenAnswer(new Answer<List<Model>>() {
//
// @Override
// public List<Model> answer(InvocationOnMock invocation) throws Throwable {
// List<Model> list = new ArrayList<Model>();
// Model model = new Model("id", "name");
// list.add(model);
// return list;
// }
//
// });
Model m = demoService.getModel("id");
assertSame(m, model);
// List<Model> result = demoService.getResults();
// assertTrue(result.size() == 1);
}
}
分享到:
相关推荐
spring集成TestNG与Mockito框架单元测试方法,方便学习者能快速上手如何单测,对自己的代码能够有质量保证。
spring集成TestNG与Mockito框架单元测试方法
本篇文章主要介绍了基于Springboot+Junit+Mockito做单元测试的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
Mockito for spring Mockito for spring Mockito for spring Mockito for spring Mockito for spring Mockito for spring Mockito for spring Mockito for spring Mockito for spring Mockito for spring Mockito ...
虽然测试分为单元测试,集成测试,系统测试等等,但是作为开发,我们可能不需要做这么多的测试(有时甚至不做……)接下来就说说和开发息息相关的单元测试以及集成测试。 单元测试就是模块测试,我的理解一个模块...
mockito junit 单元测试 mockito junit 单元测试
主要介绍了Java mockito单元测试实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
包含 Junit + Hamcrest + Mockito 单元测试涉及到的三个jar包,分别是junit.jar、hamcrest-2.2.jar、mockito-core-3.2.4.jar
内容概要:参考Mockito官方API文档,实践框架每个特性。 适合人群:Mockito入门人员以及想全面...能学到什么:“Mockito 4.6.0 + Junit 5”的组合编程。 使用建议:使用前安装好Maven及Java即可,推荐使用VSCode编辑。
简单的Mockito 使用Mockito进行单元测试 参考: :
Mockito+junit5搞定单元测试
springMVC 整合mockito单元测试学习-附件资源
Packed with real-world examples, the book covers how to make Spring code testable and mock container and external dependencies using Mockito. You then get a walkthrough of the steps that will help ...
Mockito 是单元测试中的一种常用技术,掌握Mockito对单元测试有重要影响
Mockito 3.x 需要 Java 8,但与 2.x 系列相比没有引入任何重大更改。 Mockito 2.25.0 为使用 mockito-inline 的任何人添加了一项重要功能。 特别是使用 Kotlin(需要使用 mockito-inline)和 PowerMock(这会进一步...
编程语言+JAVAspring+测试...它介绍了JAVAspring的测试框架的概念、原理和作用,以及如何使用JAVAspring的测试框架来进行单元测试,包括JUnit、TestNG、Mockito、Spring Test等内容,以及一些配置文件和注解的用法。
今天小编就为大家分享一篇关于Spring Boot单元测试中使用mockito框架mock掉整个RedisTemplate的示例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
Spring Framework 5,Junit和Mockito,Circle CI 该存储库包含用于在Junit和Mockito的帮助下测试任何应用程序的代码。 此存储库的CI在Circle CI的帮助下执行。 Junit测试: 基本注释测试 ClassPropertyTest ...
Android单元测试,结合一些测试框架(如:Robolectric、Mockito等)与Volley网络请求的单元测试
PowerMock+Mockito-junit测试套件1.6.3版本