zoukankan      html  css  js  c++  java
  • 单元测试(Spring)

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

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

    前期准备

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

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

     1     <dependencies>
     2         <dependency>
     3             <groupId>junit</groupId>
     4             <artifactId>junit</artifactId>
     5             <version>4.12</version>
     6             <scope>test</scope>
     7         </dependency>
     8         <dependency>
     9             <groupId>org.mockito</groupId>
    10             <artifactId>mockito-core</artifactId>
    11             <version>1.10.19</version>
    12             <scope>test</scope>
    13         </dependency>
    14         
    15         ………………
    16 
    17     </dependencies>
    View Code

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

    普通测试

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

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

    示例:(Controller)

     1 import org.junit.Assert;
     2 import org.junit.Before;
     3 import org.junit.Test;
     4 import org.junit.runner.RunWith;
     5 import org.springframework.beans.factory.annotation.Autowired;
     6 import org.springframework.test.context.ContextConfiguration;
     7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
     8 import org.springframework.test.context.transaction.TransactionConfiguration;
     9 import org.springframework.test.context.web.WebAppConfiguration;
    10 import org.springframework.test.web.servlet.MockMvc;
    11 import org.springframework.test.web.servlet.MvcResult;
    12 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    13 import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
    14 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    15 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    16 import org.springframework.web.context.WebApplicationContext;
    17 
    18 @WebAppConfiguration
    19 @ContextConfiguration({
    20         "classpath*:applicationContext-*.xml",
    21         "classpath*:springmvc-*.xml"})
    22 @RunWith(SpringJUnit4ClassRunner.class)
    23 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
    24 public class ControllerTest {
    25 
    26     @Autowired
    27     protected WebApplicationContext wac;
    28 
    29     @Autowired
    30     private ToTestController controller;        //需要测试的Controller
    31 
    32     private MockMvc mockMvc;
    33 
    34     private String url = "The URL to Controller";
    35     private String param1, param2, param3;
    36 
    37     private String expected = "huang";
    38 
    39     @Before
    40     public void setup() {
    41         mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();             //整个mvc环境
    42         //mockMvc = MockMvcBuilders.standaloneSetup(controller).build();        加载局部mvc环境、两种方式都可以
    43     }
    44 
    45     @Test
    46     public void testController() throws Exception {
    47         MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(url)        //传入的URL,进入对应的Controller        MockMvcRequestBuilders.get(url)
    48                             .param("param1", param1)                        //URL中携带的参数     key-value
    49                             .param("param2", param2)
    50                             .param("param3", param3))
    51                             .andExpect(MockMvcResultMatchers.view().name("user/view"))
    52                             .andExpect(MockMvcResultMatchers.model().attributeExists("user"))
    53                             .andDo(MockMvcResultHandlers.print())
    54                             .andReturn();
    55 
    56         Assert.assertNotNull(result.getModelAndView().getModel().get("user"));
    57         Assert.assertEquals(expected, result.getModelAndView().getModel().get("user"));
    58     }
    59 }
    View Code

    @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)

     1 import org.junit.Assert;
     2 import org.junit.Test;
     3 import org.junit.runner.RunWith;
     4 import org.springframework.beans.factory.annotation.Autowired;
     5 import org.springframework.test.context.ContextConfiguration;
     6 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
     7 import org.springframework.test.context.transaction.TransactionConfiguration;
     8 import org.springframework.test.context.web.WebAppConfiguration;
     9 
    10 
    11 @WebAppConfiguration
    12 @ContextConfiguration({
    13         "classpath*:applicationContext-*.xml",
    14         "classpath*:springmvc-*.xml"})
    15 @RunWith(SpringJUnit4ClassRunner.class)
    16 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
    17 public class ServiceTest {
    18 
    19 
    20     @Autowired
    21     private ToTestService service;        //需要测试的service
    22 
    23     private String expected = "huang";
    24 
    25     @Test
    26     public void testService() throws Exception {
    27         Student student = new Student("name", "sex", "school");         //创建测试方法的传入参数
    28         boolean result = service.add(student);              //需要测试的service中的add方法
    29         Assert.assertTrue(result);
    30     }
    31 }
    View Code

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

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

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

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

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

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

     1 import org.junit.Assert;
     2 import org.junit.Before;
     3 import org.junit.Test;
     4 import org.junit.runner.RunWith;
     5 import org.junit.runners.Parameterized;
     6 import org.springframework.test.context.ContextConfiguration;
     7 import org.springframework.test.context.transaction.TransactionConfiguration;
     8 import org.springframework.test.context.web.WebAppConfiguration;
     9 
    10 import java.util.Arrays;
    11 import java.util.Collection;
    12 
    13 @WebAppConfiguration
    14 @ContextConfiguration({
    15         "classpath*:applicationContext-*.xml",
    16         "classpath*:springmvc-*.xml"})
    17 @RunWith(Parameterized.class)
    18 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
    19 public class ServiceParamTest {
    20 
    21     private ToTestService service;        //需要测试的service
    22 
    23     private int input;          //为测试类声明变量,存放测试所用数据
    24     private boolean expected;       //为测试类声明变量,存放期望值
    25 
    26     @Before
    27     public void setUp() throws Exception {
    28         service = new ToTestService();
    29     }
    30 
    31     //构造函数
    32     public ServiceParamTest(int input, boolean expected) {
    33         this.input = input;
    34         this.expected = expected;
    35     }
    36 
    37     //为测试类声明一个注解@Parameters,返回值为Collection的公共静态方法,并初始化所有需要测试的参数对
    38     @Parameterized.Parameters
    39     public static Collection prepareData() {
    40         Object[][] objects = new Object[][]{{-1, true}, {0, true}, {1, true}};
    41         return Arrays.asList(objects);
    42     }
    43 
    44     //编写测试方法,使用定义的变量作为参数进行测试
    45     @Test
    46     public void testServiceParam() throws Exception {
    47         boolean result = service.excute(input);              //需要测试的service中的excute()方法
    48         Assert.assertEquals(expected, result);
    49     }
    50 }
    View Code

    参数化测试五个步骤:

    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;)

    常规用法

    模拟对象

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

    模拟方法调用的返回值

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

    模拟方法调用的参数匹配

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

    模拟方法调用抛出异常

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

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

     1 import com.pytech.mplus.cm.entity.Account;
     2 import org.junit.Assert;
     3 import org.junit.Before;
     4 import org.junit.Test;
     5 import org.junit.runner.RunWith;
     6 import org.mockito.InjectMocks;
     7 import org.mockito.Mock;
     8 import org.mockito.Mockito;
     9 import org.mockito.MockitoAnnotations;
    10 import org.springframework.beans.factory.annotation.Autowired;
    11 import org.springframework.test.context.ContextConfiguration;
    12 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    13 import org.springframework.test.context.transaction.TransactionConfiguration;
    14 import org.springframework.test.context.web.WebAppConfiguration;
    15 import org.springframework.test.web.servlet.MockMvc;
    16 import org.springframework.test.web.servlet.MvcResult;
    17 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    18 import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
    19 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    20 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    21 import org.springframework.web.context.WebApplicationContext;
    22 
    23 
    24 @WebAppConfiguration
    25 @ContextConfiguration({
    26         "classpath*:applicationContext-*.xml",
    27         "classpath*:springmvc-*.xml"})
    28 @RunWith(SpringJUnit4ClassRunner.class)
    29 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
    30 public class ControllerMockitoTest {
    31 
    32     @Autowired
    33     protected WebApplicationContext wac;
    34 
    35     @Mock
    36     private UseService service;         //ToTestController中待测试方法依赖的service
    37 
    38     @InjectMocks
    39     private ToTestController controller;        //需要测试的Controller
    40 
    41     private MockMvc mockMvc;
    42 
    43     private String url = "The URL to Controller";
    44     private String param1, param2, param3;
    45 
    46     private String expected = "huang";
    47 
    48     @Before
    49     public void setup() {
    50         MockitoAnnotations.initMocks(this);     //使得打上Mock标签的对象被mock,依赖于Mock对象的类自动与Mock对象关联
    51 
    52         mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();             //整个mvc环境
    53         //mockMvc = MockMvcBuilders.standaloneSetup(controller).build();        加载局部mvc环境、两种方式都可以
    54     }
    55 
    56     @Test
    57     public void testControllerMockito() throws Exception {
    58 
    59         Account account = Mockito.mock(Account.class);
    60         account.setAccessTime(new java.util.Date());
    61         account.setUsername("huang");
    62         account.setPhone("12312341234");
    63 
    64         Mockito.when(service.getAccountById(Mockito.anyInt())).thenReturn(account);
    65 
    66         MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(url)        //传入的URL,进入对应的Controller        MockMvcRequestBuilders.get(url)
    67                 .param("param1", param1)                        //URL中携带的参数     key-value
    68                 .param("param2", param2)
    69                 .param("param3", param3))
    70                 .andExpect(MockMvcResultMatchers.view().name("user/view"))
    71                 .andExpect(MockMvcResultMatchers.model().attributeExists("user"))
    72                 .andDo(MockMvcResultHandlers.print())
    73                 .andReturn();
    74 
    75         Assert.assertNotNull(result.getModelAndView().getModel().get("user"));
    76         Assert.assertEquals(expected, result.getModelAndView().getModel().get("user"));
    77 
    78         System.out.println(result.getModelAndView().getModel().get("user"));
    79     }
    80 }
    View Code

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

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

     1 import org.junit.Assert;
     2 import org.junit.Before;
     3 import org.junit.Test;
     4 import org.mockito.InjectMocks;
     5 import org.mockito.Mock;
     6 import org.mockito.Mockito;
     7 import org.mockito.MockitoAnnotations;
     8 import org.springframework.mock.web.MockHttpServletRequest;
     9 import org.springframework.mock.web.MockHttpServletResponse;
    10 import org.springframework.mock.web.MockHttpSession;
    11 import org.springframework.test.context.transaction.TransactionConfiguration;
    12 import org.springframework.test.web.servlet.MockMvc;
    13 import org.springframework.ui.Model;
    14 import org.springframework.web.bind.annotation.RequestParam;
    15 import org.springframework.web.servlet.ModelAndView;
    16 
    17 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
    18 public class ToTestController_toTestFunction_Test {
    19 
    20     @Mock
    21     private UsedService usedService;
    22 
    23     @InjectMocks
    24     private ToTestController controller;
    25 
    26     private MockHttpServletRequest request;
    27     private MockHttpServletResponse response;
    28     private MockHttpSession session;
    29     private ModelAndView modelAndView;
    30     private Model model;
    31 
    32     @Before
    33     public void setUp() {
    34 
    35         MockitoAnnotations.initMocks(this);
    36 
    37         request = new MockHttpServletRequest();
    38         response = new MockHttpServletResponse();
    39         session = new MockHttpSession();
    40     }
    41 
    42     @Test
    43     public void ParamsError() throws Exception {
    44         session.setAttribute("name1", "value1");
    45         session.setAttribute("name1", "value1");
    46         request.setSession(session);
    47 
    48         Info info = new Info();
    49 
    50         Mockito.when(usedService.getMerchantInfo(Mockito.anyString())).thenReturn(info);
    51         
    52         String result = controller.getStatuss(request, response, model);
    53         Assert.assertEquals("expected value", result);
    54 
    55         System.out.println(model.toString());
    56     }
    57 }
    View Code

    示例:(Service)

     1 import org.junit.Assert;
     2 import org.junit.Before;
     3 import org.junit.Test;
     4 import org.junit.runner.RunWith;
     5 import org.mockito.InjectMocks;
     6 import org.mockito.Mock;
     7 import org.mockito.Mockito;
     8 import org.mockito.MockitoAnnotations;
     9 import org.springframework.test.context.ContextConfiguration;
    10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    11 import org.springframework.test.context.transaction.TransactionConfiguration;
    12 import org.springframework.test.context.web.WebAppConfiguration;
    13 
    14 @WebAppConfiguration
    15 @ContextConfiguration({
    16         "classpath*:applicationContext-*.xml",
    17         "classpath*:springmvc-*.xml"})
    18 @RunWith(SpringJUnit4ClassRunner.class)
    19 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
    20 public class ServiceMockitoTest {
    21 
    22     @Mock
    23     private UserDao userDao;
    24 
    25     @InjectMocks
    26     private ToTestService service;
    27 
    28     @Before
    29     public void setUp() {
    30         MockitoAnnotations.initMocks(this);
    31     }
    32 
    33     @Test
    34     public void testService() throws Exception {
    35         User user = Mockito.mock(User.class);
    36         user.setName("huang");
    37         user.setEmail("The email");
    38         user.setSex("male");
    39         Mockito.when(userDao.findByEmail(Mockito.anyString())).thenReturn(user);
    40         //Mockito.when(userDao.findByEmail(Mockito.anyString())).thenReturn(null);
    41 
    42         Mockito.when(userDao.add(Mockito.any(User.class))).thenReturn(true);
    43 
    44         User u = service.getUserByEmail("The email");
    45         System.out.println(u.toString);
    46 
    47         Assert.assertTrue(service.add(user));
    48     }
    49 }
    View Code

    Maven运行指令

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

    mvn clean test

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

    mvn test -Dtest=ToTestClass

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

    mvn test -Dtest=To*Class,ToTestClass2

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

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

    JUnit插件

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

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

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

  • 相关阅读:
    CSS盒子模型
    getContextPath、getServletPath、getRequestURI、request.getRealPath的区别
    MYSQL中的CASE WHEN END AS
    单点登录的精华总结
    git&github
    June 21st 2017 Week 25th Wednesday
    June 20th 2017 Week 25th Tuesday
    June 19th 2017 Week 25th Monday
    June 18th 2017 Week 25th Sunday
    June 17th 2017 Week 24th Saturday
  • 原文地址:https://www.cnblogs.com/hthuang/p/5031249.html
Copyright © 2011-2022 走看看