單元測(cè)試(unit test)為了驗(yàn)證程序的正確性
必要性:
預(yù)防Bug,快速定位Bug,提高代碼質(zhì)量,減少耦合,減少調(diào)試時(shí)間,減少重構(gòu)風(fēng)險(xiǎn)
SpringBoot的測(cè)試庫(kù).
● Spring Test & Spring Boot Test : SpringBoot提供的應(yīng)用程序功能集成化測(cè)試支持许昨。
● Junit : Java 應(yīng)用程序單元測(cè)試標(biāo)準(zhǔn)類(lèi)庫(kù)。
● AssertJ :輕量級(jí)的斷言類(lèi)庫(kù)北启。
● Hamcrest :對(duì)象匹配器類(lèi)庫(kù)。
● Mockito : Java Mock 測(cè)試框架。
● JsonPath : JSON 操作類(lèi)庫(kù)咕村。
JSONassert :用于 JSON 的斷言庫(kù)场钉。
1.了解回歸測(cè)試框架 JUnit
JUnit 是對(duì)程序代碼進(jìn)行單元測(cè)試的 Java 框架。ビ用米癰與自切化測(cè)試工具懈涛,降低
減少煩瑣性惹悄,并有效避免出現(xiàn)程序錯(cuò)誤。
JUnit 測(cè)試是白盒測(cè)試(因?yàn)橹罍y(cè)試如何完成功能和完成什么樣的功能)肩钠。要使用只需要繼承 TestCase 類(lèi)。
JUnit 提供以下注解暂殖。
@ BeforeClass :在所有測(cè)試單元前執(zhí)行一次价匠,一般用來(lái)初始化整體的代碼。@ AfterClass :在所有測(cè)試單元后執(zhí)行一次呛每,一般用來(lái)銷(xiāo)毀和釋放資源踩窖。
@ Before :在每個(gè)測(cè)試單元前執(zhí)行,一股用來(lái)初始化方法晨横。
@ After :在每個(gè)測(cè)試單元后執(zhí)行洋腮,一般用來(lái)回滾測(cè)試數(shù)據(jù)。.@ Test :編寫(xiě)測(cè)試用例手形。
@ Test ( timeoyt =1000):對(duì)測(cè)試單元進(jìn)行限時(shí)啥供。這里的“1000”表示若超過(guò)1s
測(cè)試失敗。單位是 m 京尬女子蛋間年一點(diǎn)
@ Test ( expedted = Exception . class ):指定測(cè)試單期望得到的異常類(lèi)库糠。如果執(zhí)沒(méi)有拋出指定的異常伙狐,則測(cè)試失敗。
@ Ignore :執(zhí)行測(cè)試時(shí)將忽略掉此方法瞬欧。如果用于修飾類(lèi)贷屎,則忽略整個(gè)類(lèi)。
@ RunWith :在 JUnit 中有很多 Runner ,它們負(fù)責(zé)調(diào)用測(cè)試代碼艘虎。每個(gè) Runner 功能唉侄,應(yīng)根據(jù)需要選擇不同的 Runner 來(lái)運(yùn)行測(cè)試代碼。
這里注意:如果Assert.assertThat過(guò)期了野建,那么可以用Hamcrest.assertThat替代
創(chuàng)建單元測(cè)試 :
User
package com.example.demo;
import lombok.Data;
/**
* Copyfright(C),2022-2022,復(fù)興元宇科技有限公司
* FileName:User Author:yz Date:2022/3/18 19:29
*/
@Data
public class User {
private String name;
private int age;
}
UserService
package com.example.demo;
import org.springframework.stereotype.Service;
/**
* Copyfright(C),2022-2022,復(fù)興元宇科技有限公司
* FileName:UserService Author:yz Date:2022/3/18 19:30
*/
@Service
public class UserService {
public User getUserInfo(){
User user = new User();
user.setName("wangchaung");
user.setAge(18);
return user;
}
}
HelloController
package com.example.demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Copyfright(C),2022-2022,復(fù)興元宇科技有限公司
* FileName:HelloController Author:yz Date:2022/3/18 19:07
*/
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(String name){
return "hello " + name;
}
}
在Test目錄下創(chuàng)建類(lèi)HelloControllerTest
package com.example.demo;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
* Copyfright(C),2022-2022,復(fù)興元宇科技有限公司
* FileName:HelloControllerTest Author:yz Date:2022/3/18 19:12
*/
public class HelloControllerTest {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception{
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void hello() throws Exception{
MvcResult mvcResult = (MvcResult) mockMvc.perform(MockMvcRequestBuilders.get("/hello")
.contentType("pplication/json;charset=UTF-8")
.param("wangchuang")
.accept("pplication/json;charset=UTF-8"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("hello wangchuang"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
int status = mvcResult.getResponse().getStatus();
String content = mvcResult.getResponse().getContentAsString();
Assert.assertEquals(200,status);
Assert.assertEquals("hello wangchuang",content);
}
}
UserServiceTest運(yùn)行
package com.example.demo;
import org.hamcrest.MatcherAssert;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.regex.Matcher;
import static org.hamcrest.Matchers.is;
/**
* Copyfright(C),2022-2022,復(fù)興元宇科技有限公司
* FileName:UserServiceTest Author:yz Date:2022/3/18 19:32
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void getUserInfo(){
User user = userService.getUserInfo();
Assert.assertEquals(18,user.getAge());
// Assert.assertThat(user.getName(),is("wangchuang"));
MatcherAssert.assertThat("wancghuang",is(user.getName()));
}
}
控制臺(tái)打印運(yùn)行結(jié)果:
Service層單元測(cè)試:
User同上
服務(wù)類(lèi)如下:
package com.example.demo.service;
import com.example.demo.entity.User;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public User getUserInfo(){
User user = new User();
user.setName("zhonghua");
user.setAge(18);
return user;
}
}
編寫(xiě)測(cè)試:
同上
Respository層的單元測(cè)試
Responsitory層主要對(duì)數(shù)據(jù)進(jìn)行增刪改查
Card.class
package com.example.demo.entity;
import lombok.Data;
import javax.persistence.*;
@Entity
@Table(name = "cardtestjpa")
@Data
public class Card {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private Integer num;
}
CardRepository.interface
package com.example.demo.repository;
import com.example.demo.entity.Card;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CardRepository extends JpaRepository<Card,Long> {
Card findById(long id);
}
Service
package com.example.demo.service;
import com.example.demo.entity.Card;
import java.util.List;
public interface CardService {
public List<Card> getCardList();
public Card findCardById(long id);
}
CardServiceImpl.implements
package com.example.demo.service.impl;
import com.example.demo.entity.Card;
import com.example.demo.repository.CardRepository;
import com.example.demo.service.CardService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
public class CardServiceImpl implements CardService {
@Autowired
private CardRepository cardRepository;
@Override
public List<Card> getCardList() {
return cardRepository.findAll();
}
@Override
public Card findCardById(long id) {
return cardRepository.findById(id);
}
}
doTest
package com.example.demo.repository;
import com.example.demo.entity.Card;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
// SpringJUnit支持属划,由此引入Spring-Test框架支持!
//啟動(dòng)整個(gè)spring的工程
@SpringBootTest
//@DataJpaTest
@Transactional
//@Rollback(false)
public class CardRepositoryTest {
@Autowired
private CardRepository cardRepository;
@Test
public void testQuery() {
// 查詢操作
List<Card> list = cardRepository.findAll();
for (Card card : list) {
System.out.println(card);
}
}
@Test
public void testRollBank() {
// 查詢操作
Card card=new Card();
card.setNum(3);
cardRepository.save(card);
//throw new RuntimeException();
}
}
JPA和MySQL和單元測(cè)試junit的依賴
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
Applicaiton.properities中關(guān)于Mysql的一點(diǎn)配置
spring.datasource.url=jdbc:mysql://127.0.0.1/book?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
spring.datasource.username=root
spring.datasource.password=rootroot
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql= true
spring.thymeleaf.cache=false
server.port=8080
在IDEA中JPA的實(shí)體創(chuàng)建方式
New-JPA-Entity-[Card]
Name:類(lèi)名字
EntityType:Entity
Id:int
Idgeneration:Identity
Select OK
在IDEA中快速創(chuàng)建Responsitory
New-SpringData-Respository
Entity:上一步創(chuàng)建的[Card]
inputName
其他說(shuō)明:
@Transactional:數(shù)據(jù)回滾的意思贬墩。所有方法執(zhí)行完了以后榴嗅,回滾成原來(lái)的樣子。在實(shí)際的測(cè)試代碼中陶舞,如果將改注解注釋掉嗽测,數(shù)據(jù)是不會(huì)回滾的。
嚼一路辛苦,飲一路汗水_胫唷疏魏!