前言
随着软件行业的蓬勃发展,完成编码工作后,单元测试必不可少,但是单元测试的目的与作用还有何时如何写单元测试,这些你都知道吗?一、单元测试是什么?
- 维基百科中是这样描述的:
单元测试又称为模块测试,是针对程序模块(软件设计中的最小单元)来进行正确性检验的测试工作。 - 作用:
(1)可以减少代码bug数
(2)提高代码可读性,提高代码质量
(3)能更加理解代码逻辑设计,减少逻辑错误 - 何时写单元测试:
(1)跟随团队风格
(2)一般先写完一个功能或者一个service,一个接口就写单元测试。如果是TDD风格,那就先写测试吧
二、如何写单元测试
在单元测试框架还没有出现的年代,我们一般要测试一段代码直接写个main方法就可以了,但是如果有多个,那就很不方便了
1.现在的项目一般是springboot项目,就为大家讲解下springboot项目如何写单元测试,废话不多说,先上代码
代码如下(示例):
import com.example.entity.User;
import com.example.service.UserService;
import org.junit.*;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.*;
@SpringBootTest
@RunWith(SpringRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class UserServiceTest0 {
@Autowired
private UserService userService;
private static User user;
@BeforeClass
public static void beforeClass() {
user = new User();
user.setId( 1 );
user.setUsername( "令狐冲" );
user.setPassword( "123" );
System.out.println( "这是@BeforeClass方法" );
}
@Before
public void before() throws Exception {
System.out.println( "这是@Before方法" );
}
@Test
public void test1InsertUser() {
int i = userService.insertUser( user );
assertThat( i, is( 1 ) );
}
@Test
public void test4DeleteById() {
int i = userService.deleteUser( 1 );
assertThat( i, is( 1 ) );
}
@Test
public void test3Update() {
int i = userService.updateUser( user );
assertThat( i, is( 1 ) );
}
@Test(timeout = 1000000)
public void test2SelectById() {
User userBySearch = userService.selectById( 1 );
assertEquals( user, userBySearch );
assertNotNull( userBySearch );
assertNotSame( user, userBySearch );
assertTrue( "这两个user相等", user.equals( userBySearch ) );
assertThat( userBySearch.getUsername(), containsString( "冲" ) );
if (userBySearch==null) {
fail( "查询失败" );
}
}
@Ignore
@Test(expected = NumberFormatException.class)
public void getUserById0() {
System.out.println( userService.selectById( Integer.valueOf( "a" ) ) );
}
@After
public void after() throws Exception {
System.out.println( "这是@After方法" );
}
@AfterClass
public static void afterClass() throws Exception {
System.out.println( "这是@AfterClass方法" );
}
}
需要导入的依赖
spring-boot-starter-test,导入后下面这些类库将被一同依赖进去:
JUnit
Spring Test & Spring Boot Test
AssertJ
Hamcrest
Mockito
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
在这里讲解下各个注解的作用及流程
1).@SpringBootTest
基本等同于启动了整个服务,此时便可以开始功能测试。
属性值:classes,webEnvironment
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
(1)classes 指定配置文件,一般不写。默认去找application.yml中指定的配置文件,替代@ContextConfiguration(locations={“classpath:applicationContext.xml”}的用法
(2)webEnvironment 有4个属性值,大家看需求选择,如果只是做功能测试,直接写@SpringBootTest即可,4个属性值分别是:
mock:此值为默认值,该类型提供一个mock环境,时内嵌的服务(servlet容器)并没有真正启动,也不会监听web服务端口。
RANDOM_PORT:启动一个真实的web服务,监听一个随机端口
DEFINED_PORT:启动一个真实的web服务,监听一个定义好的端口
NONE:启动一个非web的ApplicationContext,既不提供mock环境,也不 提供真实的web服务
2.Junit中的常用注释
1)@BeforeClass 全局只会执行一次,而且是第一个运行
2)@Before 在测试每一个具体方法运行之前运行
3)@Test 测试具体方法
expected属性
timeout属性
4)@After 在测试每一个具体方法运行之后=运行
5)@AfterClass 全局只会执行一次,而且是最后一个运行
6)@Ignore 忽略此方法
3.运行器指定@RunWith
1)@RunWith(JUnit4.class)就是指用JUnit4来运行
2)@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境,一般使用@RunWith(SpringRunner.class)
3.@RunWith(Suite.class)的话就是一套测试集合
@RunWith( Suite.class )
@Suite.SuiteClasses( {UserServiceTest.class,MockTest.class} )
4.@RunWith(Parameterized.class) 参数化运行器,配合@Parameters使用junit的参数化功能
5.Mockito
(1)使用场景:测试对象依赖很多,难以模拟或者模拟不出来;所依赖的功能还没有写完
(2)上代码:
@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class UserServiceTest1 {
@MockBean
private UserService userService;
private User user = mock(User.class);
@BeforeClass
public static void beforeClass() {
System.out.println( "这是@BeforeClass方法" );
}
@Before
public void before() throws Exception {
System.out.println( "这是@Before方法" );
}
@Test
public void test1InsertUser() {
int i = userService.insertUser( user );
assertThat( i, is( 1 ) );
}
@Test
public void test1InsertUserTest() {
int a = 0;
User mock = mock( User.class );
int i = userService.insertUser( mock );
}
@Test
public void test1InsertUserTest1() {
when( userService.insertUser( user ) ).thenReturn( 1 );
int i = userService.insertUser( user );
assertThat( i, is( 1 ) );
}
@Test
public void test1InsertUserTest2() {
doReturn( 1 ).when( userService ).insertUser( user );
int i = userService.insertUser( user );
assertThat( i, is( 1 ) );
verify(userService).insertUser(user);
}
@Test
public void test4DeleteById() {
int i = userService.deleteUser( 1 );
assertThat( i, is( 1 ) );
}
@Test
public void test3Update() {
int i = userService.updateUser( user );
assertThat( i, is( 1 ) );
}
@Test(timeout = 1000000)
public void test2SelectById() {
User userBySearch = userService.selectById( 1 );
assertEquals( user, userBySearch );
assertNotNull( userBySearch );
assertNotSame( user, userBySearch );
assertTrue( "这两个user相等", user.equals( userBySearch ) );
assertThat( userBySearch.getUsername(), containsString( "冲" ) );
if (userBySearch==null) {
fail( "查询失败" );
}
}
@Ignore
@Test(expected = NumberFormatException.class)
public void getUserById0() {
System.out.println( userService.selectById( Integer.valueOf( "a" ) ) );
}
@After
public void after() throws Exception {
System.out.println( "这是@After方法" );
}
@AfterClass
public static void afterClass() throws Exception {
System.out.println( "这是@AfterClass方法" );
}
}
总结
今天就分享到这里,下次为大家介绍断言与mockito的打桩