单元测试是指对软件中的最小可测试单元进行的检查和验证,是软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

单元测试好处:提高代码质量(实现功能、逻辑严密)、减少调试时间、隔离测试。

前期准备

单元测试工具类很多,一般选择流行的Junit和Mockito进行测试演示。如果进行普通组合测试可不用Mockito,隔离测试则需用到Mockito。

首先,引入相关Jar包 --- Junit 和 org.mockito。如果是使用Maven工程,则需在pom.xml文件中引入依赖。参考样式如下:

     <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency> ……………… </dependencies>

其次,建立测试类。可通过JUnit工具生成测试类(可设置生成的测试类位置),也可手动创建。一般情况,建议一个java类对应一个测试类,但如果需针对一个方法进行多方面测试,则可针对一个类中一个方法创建一个测试类。例如:测试找不到控制器,404测试、验证请求参数绑定、验证请求参数验证失败、JSON请求、响应异常处理等等。

普通测试

普通测试是将整个大的模块整体一起测试。如目标测试Controller层,但Control层依赖Service层,而Service层又依赖DAO层,则我们对Controller进行普通测试时,便连同Serice层和DAO层也一起测试了。

常规步骤:(1)参数赋值  (2)写出期望值  (3)获取实际值  (4)断言 --- 比较期望值与实际值是否相同

示例:(Controller)

 import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; @WebAppConfiguration
@ContextConfiguration({
"classpath*:applicationContext-*.xml",
"classpath*:springmvc-*.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
public class ControllerTest { @Autowired
protected WebApplicationContext wac; @Autowired
private ToTestController controller; //需要测试的Controller private MockMvc mockMvc; private String url = "The URL to Controller";
private String param1, param2, param3; private String expected = "huang"; @Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); //整个mvc环境
//mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); 加载局部mvc环境、两种方式都可以
} @Test
public void testController() throws Exception {
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(url) //传入的URL,进入对应的Controller MockMvcRequestBuilders.get(url)
.param("param1", param1) //URL中携带的参数 key-value
.param("param2", param2)
.param("param3", param3))
.andExpect(MockMvcResultMatchers.view().name("user/view"))
.andExpect(MockMvcResultMatchers.model().attributeExists("user"))
.andDo(MockMvcResultHandlers.print())
.andReturn(); Assert.assertNotNull(result.getModelAndView().getModel().get("user"));
Assert.assertEquals(expected, result.getModelAndView().getModel().get("user"));
}
}

@WebAppConfiguration:测试环境使用,表示测试环境使用的ApplicationContext是WebApplicationContext类型的。@WebAppConfiguration(value = "src/main/webapp") 中value指定web应用的根;

@ContextConfiguration:指定Spring配置文件或者配置类的位置;

@RunWith(SpringJUnit4ClassRunner.class):启动Spring对测试类的支持;

@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true):启用自动的事务管理,事务回滚;

@Autowired:自动织入 Spring 的 bean 用来测试;

@Before:每个方法测试前调用。一般用于public void setup() { mockMvc=MockMvcBuilders.standaloneSetup(wac).build();}前面;

@Test:测试方法,表明这是一个测试方法。在Junit中将会自动被执行。

MockMvc:在setUp(){}方法中,通过MockMvcBuilders.webAppContextSetup(wac).build()创建一个MockMvc进行测试;

mockMvc.perform():执行一个请求;

MockMvcRequestBuilders.post(url).param("param1", param1):构造一个请求,请求可传带参数;

ResultActions.andExpect():添加执行完成后的断言,ResultMatcher验证规则,验证控制器执行完成后结果是否正确;

ResultActions.andDo():添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息;

ResultActions.andReturn():表示执行完成后返回相应的结果;

Assert.  :通过静态方法执行断言,判断测试结果与预期是否相同。

示例:(Service、DAO)

 import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration; @WebAppConfiguration
@ContextConfiguration({
"classpath*:applicationContext-*.xml",
"classpath*:springmvc-*.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
public class ServiceTest { @Autowired
private ToTestService service; //需要测试的service private String expected = "huang"; @Test
public void testService() throws Exception {
Student student = new Student("name", "sex", "school"); //创建测试方法的传入参数
boolean result = service.add(student); //需要测试的service中的add方法
Assert.assertTrue(result);
}
}

对于Service、DAO仅仅是测试用,则需要加上事务回滚

 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)

对Service、DAO的测试相对于Controller来说要简单很多,大部分内容都在Controller里面讲过,不同的地方就是Controller是使用mockMvc对象来模拟Controler的被测方法,而在Service的单元测试中则是直接调用Service的方法。

针对DAO层测试方法和Service层测试方法类似。

示例:(Service、DAO)参数化测试

参数化测试主要是用于测试分支语句,多个参数覆盖if…else等判断语句中的分支,使测试更全面。

 import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration; import java.util.Arrays;
import java.util.Collection; @WebAppConfiguration
@ContextConfiguration({
"classpath*:applicationContext-*.xml",
"classpath*:springmvc-*.xml"})
@RunWith(Parameterized.class)
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
public class ServiceParamTest { private ToTestService service; //需要测试的service private int input; //为测试类声明变量,存放测试所用数据
private boolean expected; //为测试类声明变量,存放期望值 @Before
public void setUp() throws Exception {
service = new ToTestService();
} //构造函数
public ServiceParamTest(int input, boolean expected) {
this.input = input;
this.expected = expected;
} //为测试类声明一个注解@Parameters,返回值为Collection的公共静态方法,并初始化所有需要测试的参数对
@Parameterized.Parameters
public static Collection prepareData() {
Object[][] objects = new Object[][]{{-1, true}, {0, true}, {1, true}};
return Arrays.asList(objects);
} //编写测试方法,使用定义的变量作为参数进行测试
@Test
public void testServiceParam() throws Exception {
boolean result = service.excute(input); //需要测试的service中的excute()方法
Assert.assertEquals(expected, result);
}
}

参数化测试五个步骤:

(1)为准备使用参数化测试的测试类指定特殊的运行器org.junit.runners.Parameterized;

(2)为测试类声明几个变量,分别用于存放期望值和测试所用数据;

(3)为测试类声明一个带有参数的公共构造函数,并在其中为第二个环节中声明的几个变量赋值;

(4)为测试类声明一个使用注解org.junit.runners.Parameterized.Parameters修饰的,返回值为 java.util.Collection的公共静态方法,并在此方法中初始化所有需要测试的参数对;

(5)编写测试方法,使用定义的变量作为参数进行测试。

隔离测试

除了普通的整体测试之外,我们经常要用到的隔离测试,也即是防止多类之间依赖的测试。例如当测试Controller层时,Controller层依赖Service层,而Service层又依赖于DAO层。这时,可以利用Mockito来进行隔离,单独测试Controller。

主要思想是利用Mockito --- 模拟。(需导入import org.mockito.Mockito;)

常规用法

模拟对象

         LinkedList mockedList = Mockito.mock(LinkedList.class);         //模拟LinkedList对象
System.out.println(mockedList.get(1)); //返回值为null,没有对方法调用的返回值做模拟

模拟方法调用的返回值

         Mockito.when(mockedList.get(0)).thenReturn("first");
System.out.println(mockedList.get(0));

模拟方法调用的参数匹配

         Mockito.when(mockedList.get(Mockito.anyInt())).thenReturn("element");       // anyInt()匹配任何int参数,这意味着参数为任意值
System.out.println(mockedList.get(0)); // 此时打印是element

模拟方法调用抛出异常

         Mockito.when(mockedList.get(1)).thenThrow(new RuntimeException());
System.out.println(mockedList.get(1)); Mockito.doThrow(new RuntimeException()).when(mockedList).clear(); //没有返回值类型的方法模拟抛出异常

示例:(Controller) -- 通过拦截器,测试Controller功能

 import com.pytech.mplus.cm.entity.Account;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; @WebAppConfiguration
@ContextConfiguration({
"classpath*:applicationContext-*.xml",
"classpath*:springmvc-*.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
public class ControllerMockitoTest { @Autowired
protected WebApplicationContext wac; @Mock
private UseService service; //ToTestController中待测试方法依赖的service @InjectMocks
private ToTestController controller; //需要测试的Controller private MockMvc mockMvc; private String url = "The URL to Controller";
private String param1, param2, param3; private String expected = "huang"; @Before
public void setup() {
MockitoAnnotations.initMocks(this); //使得打上Mock标签的对象被mock,依赖于Mock对象的类自动与Mock对象关联 mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); //整个mvc环境
//mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); 加载局部mvc环境、两种方式都可以
} @Test
public void testControllerMockito() throws Exception { Account account = Mockito.mock(Account.class);
account.setAccessTime(new java.util.Date());
account.setUsername("huang");
account.setPhone("12312341234"); Mockito.when(service.getAccountById(Mockito.anyInt())).thenReturn(account); MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(url) //传入的URL,进入对应的Controller MockMvcRequestBuilders.get(url)
.param("param1", param1) //URL中携带的参数 key-value
.param("param2", param2)
.param("param3", param3))
.andExpect(MockMvcResultMatchers.view().name("user/view"))
.andExpect(MockMvcResultMatchers.model().attributeExists("user"))
.andDo(MockMvcResultHandlers.print())
.andReturn(); Assert.assertNotNull(result.getModelAndView().getModel().get("user"));
Assert.assertEquals(expected, result.getModelAndView().getModel().get("user")); System.out.println(result.getModelAndView().getModel().get("user"));
}
}

要做到隔离,就得模拟其依赖部分。当测试方法需调用依赖方法时便返回模拟值,从而达到隔离测试的目的。

示例:(Controller) -- 直接绕过系统拦截器,测试Controller功能

 import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView; @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
public class ToTestController_toTestFunction_Test { @Mock
private UsedService usedService; @InjectMocks
private ToTestController controller; private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MockHttpSession session;
private ModelAndView modelAndView;
private Model model; @Before
public void setUp() { MockitoAnnotations.initMocks(this); request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
session = new MockHttpSession();
} @Test
public void ParamsError() throws Exception {
session.setAttribute("name1", "value1");
session.setAttribute("name1", "value1");
request.setSession(session); Info info = new Info(); Mockito.when(usedService.getMerchantInfo(Mockito.anyString())).thenReturn(info); String result = controller.getStatuss(request, response, model);
Assert.assertEquals("expected value", result); System.out.println(model.toString());
}
}

示例:(Service)

 import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration; @WebAppConfiguration
@ContextConfiguration({
"classpath*:applicationContext-*.xml",
"classpath*:springmvc-*.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
public class ServiceMockitoTest { @Mock
private UserDao userDao; @InjectMocks
private ToTestService service; @Before
public void setUp() {
MockitoAnnotations.initMocks(this);
} @Test
public void testService() throws Exception {
User user = Mockito.mock(User.class);
user.setName("huang");
user.setEmail("The email");
user.setSex("male");
Mockito.when(userDao.findByEmail(Mockito.anyString())).thenReturn(user);
//Mockito.when(userDao.findByEmail(Mockito.anyString())).thenReturn(null); Mockito.when(userDao.add(Mockito.any(User.class))).thenReturn(true); User u = service.getUserByEmail("The email");
System.out.println(u.toString); Assert.assertTrue(service.add(user));
}
}

Maven运行指令

可以通过在命令行中输入指令来运行所有的测试用例,命令如下:

mvn clean test

可以直接在命令行中输入动态指令来运行指定的测试用例,命令如下:

mvn test -Dtest=ToTestClass

也可以使用通配符,也可以使用“,”指定多个测试类,如下所示:

mvn test -Dtest=To*Class,ToTestClass2

指定测试方法: 使用#指定测试方法,使用*通配测试方法,使用+号指定一个类中的多个测试方法

 mvn test  -Dtest=ToTestClass#testABC,ToTestCl*s2
mvn test -Dtest=ToTestClass#testABC,ToTestClass2
mvn test -Dtest=ToTestClass#testABC+testEFG,ToTestClass2

JUnit插件

一些常用的IDE工具都带有JUnit插件,如Eclipse、 IntelliJ IDEA。

Eclipse:选中要运行的类(或光标放在要运行的测试方法上) --> 点击右键 --> Run as --> JUnit 即可运行。

IDEA:选中要运行的类(或光标放在要运行的测试方法上) --> 点击右键 --> Run。

单元测试(Spring)的更多相关文章

  1. Spring Boot 入门之单元测试篇(五)

    博客地址:http://www.moonxy.com 一.前言 JUnit 是一个由 Java 语言编写的开源的回归测试(回归测试是指重复以前全部或部分的相同测试)框架,由Erich Gamma 和 ...

  2. Spring Boot 的单元测试和集成测试

    学习如何使用本教程中提供的工具,并在 Spring Boot 环境中编写单元测试和集成测试. 1. 概览 本文中,我们将了解如何编写单元测试并将其集成在 Spring Boot 环境中.你可在网上找到 ...

  3. Spring Boot干货系列:(十二)Spring Boot使用单元测试(转)

    前言这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求 Service层单元测试 Controller层单元测试 新断言assertThat使用 单元 ...

  4. Spring Boot实战之单元测试

    Spring Boot实战之单元测试 本文介绍使用Spring测试框架提供的MockMvc对象,对Restful API进行单元测试 Spring测试框架提供MockMvc对象,可以在不需要客户端-服 ...

  5. Spring+SpringMVC+MyBatis+easyUI整合

    进阶篇 Spring+SpringMVC+MyBatis+easyUI整合进阶篇(一)设计一套好的RESTful API 优化篇 Spring+SpringMVC+MyBatis+easyUI整合优化 ...

  6. Spring+SpringMVC+MyBatis+easyUI整合优化篇

    优化篇 Spring+SpringMVC+MyBatis+easyUI整合优化篇(一)System.out.print与Log Spring+SpringMVC+MyBatis+easyUI整合优化篇 ...

  7. SpringBoot项目单元测试

    关于SpringBoot的单元测试,描述一下三种单元测试的方式. 1.约定 单元测试代码写在src/test/java目录下单元测试类命名为*Test,前缀为要测试的类名 2. 使用mock方式单元测 ...

  8. Spring+SpringMVC+MyBatis整合(easyUI、AdminLte3)

    实战篇(付费教程) 花了几天的时间,做了一个网站小 Demo,最终效果也与此网站类似.以下是这次实战项目的 Demo 演示. 登录页: 富文本编辑页: 图片上传: 退出登录: SSM 搭建精美实用的管 ...

  9. Spring+SpringMVC+MyBatis整合优化篇

    优化篇 Spring+SpringMVC+MyBatis+easyUI整合优化篇(一)System.out.print与Log Spring+SpringMVC+MyBatis+easyUI整合优化篇 ...

  10. Spring链接汇总

    Spring Boot专题 基础入门内容 SpringBoot快速入门 Why Spring Boot 使用Intellij中的Spring Initializr来快速构建Spring Boot/Cl ...

随机推荐

  1. JavaScript 格式化时间

    //格式化 yyyy-MM-dd hh:mm:ss function renderTime(date) { if (date == '' || date == null) { return ''; } ...

  2. Android批量验证渠道、版本号

    功能:可校验单个或目录下所有apk文件的渠道号.版本号使用说明:1.copy需要校验的apk文件到VerifyChannelVersion目录下2.双击运行VerifyChannelVersion.b ...

  3. 自动生成数学题型一 (框架Struts2) 题型如(a+b=c)

    1. 加减乘除 1.1 随机生成制定范围的整数 /** * 随机产生一个被限定范围的整数 * * @param num1 * 定义起始范围 num1 * @param num2 * 定义终止范围 nu ...

  4. Filter execution threw an exception 错误

    Filter execution threw an exception 错误,我在web.xml中配置了一个filter用spring来解决懒加载的问题,为什么就会包这个错 java.lang.NoS ...

  5. CentOS6.5 配置本地Yum源

    一.Yum简介 1.Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器. 2.基于RPM包管理, ...

  6. callLater

    UIComponent的方法,该方法在每次更新屏幕之前,Flash Player 或 AIR 都会调用为更新预定的函数集.有时,应在下次更新时调用函数,以执行为当前更新预定的其余代码.部分功能(如效果 ...

  7. js的几大数据类型

    一. js的几大数据类型 数字:浮点数(3.14)+整数(1): 字符串:包括由任意数量字符组成的序列,例如:'a','one': 布尔值:true+false: undefined:当我们试图访问一 ...

  8. DFB系列 之 Bilp叠加

    1. 函数原型解析 函数声明: DFBResult Blit (     IDirectFBSurface    *  thiz,      IDirectFBSurface    *  source ...

  9. Vue2.x中的父组件数据传递至子组件

    父组件结构 template <template> <div> <v-girl-group :girls="aGirls"></v-gir ...

  10. CSS绘制简单图形

    究竟该用字体图标.图片图标.还是CSS画一个图标?我也不知道.各有千秋吧.本文将介绍如何用css绘制简单的图形,所有测试在chrome58.0完成,如果你不能得到正确结果请到caniuse查一查看看是 ...