一.要解决的问题:
spring环境中单元测试其实不是真正意义上的单元测试,真正意义上的单元测试应该是隔离所有的依赖,对当前业务实现业务逻辑测试;但是目前spring好像还没提供这样的解决方案,只能做依赖于环境的集成测试。比如:要测试A类,但是A类依赖B类和C类,这个时候我们必须保证B和C是完整的且是相对稳定的没太多bug的类.但是实际开发过程中,C类和B类可能是对数据库操作的Dao层或是对外接口层,这个时候我们在测试A类的时候业务B和C的环境或B或C都现在还没开发完成只是一个接口定义完成,这个时候就很难完成我们A类的测试了。
二.解决方案:
为了解决这个问题我们必须在测试的时候忽略B和C类,换句话说就是假象B和C都是可以运行或按我们预期返回结果的运行。我们利用mockito来掩饰我们测试类的所有的依赖。这样我们需要做到两点1.我们可以让B和C可以控制返回预期;2.B和C必须注入到spring中替换我们的测试类的依赖.
/**
* 类SayServiceTests.java的实现描述:Mock demo
*
*/
@ContextConfiguration(locations = { "/applicationContext.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({ org.springframework.test.context.support.DependencyInjectionAsMockitoTestExecutionListener.class })
public class SayServiceTest {
@Mock
public SayDao sayDao;
@Autowired
public SayService sayService; // TODO 暂时用实现类
@Test
public void testSay() {
// 1.设置预期行为 void
when(sayDao.sayTo(null)).thenReturn("3");
// 2.验证
assertTrue(sayService.sayTo(null).equals("3"));
}
public SayDao getSayDao() {
return sayDao;
}
public void setSayDao(SayDao sayDao) {
this.sayDao = sayDao;
}
public SayService getSayService() {
return sayService;
}
public void setSayService(SayService sayService) {
this.sayService = sayService;
}
@Test
public void testSayTo() {
System.out.println("testSayTo...");
// 1.设置预期行为
when(sayDao.sayTo(any(Person.class))).thenAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
// SayDao mock = (SayDao)invocation.getMock(); //Object mock = invocation.getMock();
if (null != args && args.length > 0) {
Person person = (Person) args[0];
return person.getName();
}
return null;
}
});
// 2.验证
Person person = new Person();
person.setId(11);
person.setName("Leifeng");
String s = sayService.sayTo(person);
System.out.println(s);
assertSame("Leifeng", s);
}
@Test
public void testSaySomething() {
System.out.println("testSaySomething...");
// 1.设置预期行为
when(sayDao.saySomething(anyString(), any(Person.class), anyString())).thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
if (null != args && args.length > 0) {
String hello = (String) args[0];
Person person = (Person) args[1];
String msg = (String) args[2];
return hello + "," + person.getName() + "(" + person.getId() + ")" + ":" + msg;
// SayDao dao = new SayDaoImpl();
// return dao.saySomething(hello, person, msg);
}
return null;
}
});
// 2.验证
Person person = new Person();
person.setId(12);
person.setName("Leifeng");
String s = sayService.saySomething("Welcome", person, "handsome guy!");
System.out.println(s);
assertNotNull(s);
}
@Test
public void testQueryPerson() {
// 1.预置预期行为
List<Person> personList = new ArrayList<Person>();
// 初始化List《Person》
for (int i = 0; i < 10; i++) {
Person person = new Person();
person.setId(i + 1);
person.setName("name" + i);
personList.add(person);
}
when(sayDao.queryPerson(any(Person.class))).thenReturn(personList);
// 2.验证
Person query = new Person();
query.setId(13);
query.setName("Leifeng");
List<Person> list = sayService.queryPerson(query);
assertTrue(10 == list.size());
// 重要(根据具体业务设计)
assertTrue(3 == list.get(3).getFlag());
}
}
DependencyInjectionAsMockitoTestExecutionListener类是在spring-test中的DependencyInjectionTestExecutionListener基础上扩展的一个结合mock的测试监听器。我们在测试的时候可以用注解TestExecutionListeners指定这个监听器来实现单元测试
package org.springframework.test.context.support;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.Mockito.mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestContext;
/**
* @author yanglin lv
*/
public class DependencyInjectionAsMockitoTestExecutionListener extends DependencyInjectionTestExecutionListener {
private static String SETTER = "set";
private static String GETTER = "get";
@Override
protected void injectDependencies(final TestContext testContext) throws Exception {
super.injectDependencies(testContext);
Object bean = testContext.getTestInstance();
Class[] mockClass = getMockClass(bean.getClass());
Method[] methods = bean.getClass().getDeclaredMethods();
Class clz = bean.getClass();
Object instance = null;
List<MockObjectMap> objs = new ArrayList<MockObjectMap>();
autowireMockBean(clz, bean, objs);
List<Object> stubObjs = getStubInstance(clz, bean);
autowireMockBeanForSpring(stubObjs, objs);
}
private void autowireMockBeanForSpring(List<Object> stubObjs, List<MockObjectMap> objs)
throws IllegalArgumentException,
IllegalAccessException,
InvocationTargetException {
for (Object object : stubObjs) {
Class claz = object.getClass();
do {
for (Method method : claz.getDeclaredMethods()) {
if (method.getName().startsWith(SETTER)) {
for (MockObjectMap mockObjectMap : objs) {
Object obj = method.getGenericParameterTypes()[0];
if (obj instanceof java.lang.reflect.Type
&& mockObjectMap.getType().getName().equalsIgnoreCase(((Class) obj).getName())) {
method.invoke(object, mockObjectMap.getObj());
continue;
}
}
}
}
claz = claz.getSuperclass();
} while (!claz.equals(Object.class));
}
}
private void autowireMockBean(Class clz, Object bean, List<MockObjectMap> objs) throws IllegalArgumentException,
IllegalAccessException {
for (Field field : clz.getFields()) {
Annotation[] mockAnnotations = field.getAnnotations();
for (Annotation annotation : mockAnnotations) {
if (annotation instanceof org.mockito.Mock) {
MockObjectMap mockObjectMap = new MockObjectMap();
objs.add(mockObjectMap);
mockObjectMap.setType(field.getType());
mockObjectMap.setObj(mock(field.getType()));
field.setAccessible(true);
field.set(bean, mockObjectMap.getObj());
continue;
}
}
}
}
/**
* 取得测试类中所有的mock对象的类型
*
* @param clazz
* @return
*/
private Class[] getMockClass(Class claz) {
List<Class> clasList = new ArrayList<Class>();
Field[] fields = claz.getDeclaredFields();
for (Field field : fields) {
Annotation[] mockAnnotations = field.getAnnotations();
for (Annotation annotation : mockAnnotations) {
if (annotation instanceof org.mockito.Mock) {
clasList.add(field.getType());
continue;
}
}
}
return clasList.toArray(new Class[0]);
}
/**
* 取得测试类中测试桩类
*
* @param clazz
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
private List<Object> getStubInstance(Class clazz, Object bean) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
List<Object> objList = new ArrayList<Object>();
Field[] fields = clazz.getDeclaredFields();// 测试类中所有的域名
Method[] methods = clazz.getDeclaredMethods();
for (Field field : fields) {
Annotation[] mockAnnotations = field.getAnnotations();
for (Annotation annotation : mockAnnotations) {
if (annotation instanceof Autowired) {
for (Method method : methods) {
String name = field.getName();
if (method.getName().startsWith(GETTER) && method.getName().substring(3).equalsIgnoreCase(name)) {
objList.add(method.invoke(bean, null)); // 将所有的测试桩类放在objList
}
}
}
}
}
return objList;
}
private class MockObjectMap {
private Object obj;
private Class<?> type;
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;
}
}
}
总结:这种方式可以真正的用spring来实现TDD面向接口的测试方案,对依赖的类做到完全屏蔽,对目前测试类和mock类设置期望输出简单实现
- 大小: 10.9 KB
- 大小: 12 KB
分享到:
相关推荐
spring-boot-starter-test-1.0.2.RELEASE.jar 各个版本,免费下载 spring-boot-starter-test-RELEASE.jar 各个版本,免费下载 spring-boot-starter-test.jar 各个版本,免费下载 如果不能免费下载,关注我,评论区...
赠送jar包:spring-boot-test-autoconfigure-2.5.5.jar; 赠送原API文档:spring-boot-test-autoconfigure-2.5.5-javadoc.jar; 赠送源代码:spring-boot-test-autoconfigure-2.5.5-sources.jar; 赠送Maven依赖信息...
赠送jar包:spring-test-5.2.7.RELEASE.jar; 赠送原API文档:spring-test-5.2.7.RELEASE-javadoc.jar; 赠送源代码:spring-test-5.2.7.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-test-5.2.7.RELEASE....
赠送jar包:spring-boot-test-autoconfigure-2.0.6.RELEASE.jar; 赠送原API文档:spring-boot-test-autoconfigure-2.0.6.RELEASE-javadoc.jar; 赠送源代码:spring-boot-test-autoconfigure-2.0.6.RELEASE-sources...
赠送jar包:spring-boot-test-autoconfigure-2.2.8.RELEASE.jar; 赠送原API文档:spring-boot-test-autoconfigure-2.2.8.RELEASE-javadoc.jar; 赠送源代码:spring-boot-test-autoconfigure-2.2.8.RELEASE-sources...
赠送jar包:spring-boot-test-autoconfigure-2.0.4.RELEASE.jar; 赠送原API文档:spring-boot-test-autoconfigure-2.0.4.RELEASE-javadoc.jar; 赠送源代码:spring-boot-test-autoconfigure-2.0.4.RELEASE-sources...
综上所述,"Spring-test做数据库操作的单元测试2-跨库访问"这个主题涵盖了许多关键概念,包括Spring-test的运行机制、多数据源配置、事务管理以及测试的准备与清理。这些知识点对于开发涉及跨库操作的应用程序,确保...
赠送jar包:spring-boot-test-2.6.3.jar; 赠送原API文档:spring-boot-test-2.6.3-javadoc.jar; 赠送源代码:spring-boot-test-2.6.3-sources.jar; 赠送Maven依赖信息文件:spring-boot-test-2.6.3.pom; 包含...
赠送jar包:spring-test-5.3.15.jar; 赠送原API文档:spring-test-5.3.15-javadoc.jar; 赠送源代码:spring-test-5.3.15-sources.jar; 赠送Maven依赖信息文件:spring-test-5.3.15.pom; 包含翻译后的API文档:...
赠送jar包:spring-test-5.0.5.RELEASE.jar; 赠送原API文档:spring-test-5.0.5.RELEASE-javadoc.jar; 赠送源代码:spring-test-5.0.5.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-test-5.0.5.RELEASE....
赠送jar包:spring-boot-test-2.2.8.RELEASE.jar; 赠送原API文档:spring-boot-test-2.2.8.RELEASE-javadoc.jar; 赠送源代码:spring-boot-test-2.2.8.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-boot-...
赠送jar包:spring-boot-test-2.2.8.RELEASE.jar; 赠送原API文档:spring-boot-test-2.2.8.RELEASE-javadoc.jar; 赠送源代码:spring-boot-test-2.2.8.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-boot-...
在SSM环境中,使用Junit4和spring-test库进行单元测试是标准做法。下面将详细解释如何使用这两个库以及所需的jar包。 Junit4是Java领域广泛使用的单元测试框架,它提供了一套丰富的注解,使得编写测试用例变得更加...
在`3.2.0.RELEASE`这个版本中,Spring Test进一步增强了对Mock对象的支持,优化了测试环境的配置,提升了测试的效率和覆盖率。 1. **测试支持:** Spring Test提供了`@ContextConfiguration`注解,用于加载测试所...
赠送jar包:spring-test-5.0.5.RELEASE.jar; 赠送原API文档:spring-test-5.0.5.RELEASE-javadoc.jar; 赠送源代码:spring-test-5.0.5.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-test-5.0.5.RELEASE....
赠送jar包:spring-test-4.3.20.RELEASE.jar; 赠送原API文档:spring-test-4.3.20.RELEASE-javadoc.jar; 赠送源代码:spring-test-4.3.20.RELEASE-sources.jar; 包含翻译后的API文档:spring-test-4.3.20....
综上所述,"spring-boot-test"示例程序涵盖了Spring Boot中测试的核心概念,包括Rest Controller的集成测试、单元测试的策略以及测试工具的使用,这些都是保证软件质量和可维护性的重要组成部分。通过学习和实践这些...
赠送jar包:spring-boot-test-2.0.6.RELEASE.jar; 赠送原API文档:spring-boot-test-2.0.6.RELEASE-javadoc.jar; 赠送源代码:spring-boot-test-2.0.6.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-boot-...
6. **spring-test-3.2.0.RELEASE.jar**:测试模块提供了对JUnit和TestNG的支持,使得在Spring环境中进行单元测试和集成测试变得简单。它包含`@ContextConfiguration`等注解,用于加载和配置测试用例中的...
赠送jar包:spring-test-4.2.2.RELEASE.jar; 赠送原API文档:spring-test-4.2.2.RELEASE-javadoc.jar; 赠送源代码:spring-test-4.2.2.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-test-4.2.2.RELEASE....