【Spring Boot】使用JUnit×DBUnit进行Controller类、Service类和Repository类的测试
这是使用JUnit和DBUnit来测试Spring Boot的Controller类、Service类和Repository类的示例代码。
环境
-
- OS:Windows10
-
- IDE:Eclipse2020-03
-
- Java:8
-
- MySQL:5.7
-
- Spring Boot:2.3.0
-
- JUnit:5
- DBUnit:2.7.0
层次结构
spring_boot_test_item
|
src/main/java
|----jp.co.test_item
| |----app
| | |---- controller
| | |---- response
| |
| |----domain
| |---- service
| |---- repository
| |---- model
|
src/main/resources
|----application.yml
|
|
src/main/test(※)
基本上,位于src/main/test目录下的文件与src/main/java目录下的文件具有相同的结构,所以可以省略不提。
创建表
CREATE TABLE `items` (
`item_id` int(11) NOT NULL AUTO_INCREMENT,
`item_name` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
`item_explanation` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
`category_id` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
执行以上的DDL语句,创建表。
应用程序配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://接続先名/DB名?serverTimezone=JST&nullCatalogMeansCurrent=true
username: ユーザー名
password: パスワード
jpa:
database: MYSQL
hibernate.ddl-auto: update
main:
allow-bean-definition-overriding: true
在此中写下连接到MySQL的信息。
重点有以下两点。
-
- 通过将”nullCatalogMeansCurrent=true”设置为真来避免在同一DB实例的其他数据库中存在同名表时产生错误。
- 通过将”allow-bean-definition-overriding: true”(还要记得加上上面的”main:”)来允许bean的重写。
build.gradle的配置
plugins {
id 'org.springframework.boot' version '2.3.0.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'jp.co.test_item'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencyManagement {
imports {
mavenBom "org.junit:junit-bom:5.6.2"//BOMのインポート
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
compile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.4'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.junit.jupiter:junit-jupiter'//JUnit5に対応
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'//IDEなどのサポート用
testCompile group: 'com.github.springtestdbunit', name: 'spring-test-dbunit', version: '1.3.0' // SpringでDBUnitを用いる際に必要
testCompile group: 'org.dbunit', name: 'dbunit', version: '2.7.0' // DBUnitのAPI
}
test {
useJUnitPlatform()
}
这次的重点在于评论的部分。
考察的实际实现
编写实现目标类群的代码。
1. 控制器类
package jp.co.spring_boot_test_item.app.controller;
import java.sql.SQLException;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import jp.co.spring_boot_test_item.app.request.ItemPostRequest;
import jp.co.spring_boot_test_item.app.response.ItemResponse;
import jp.co.spring_boot_test_item.app.response.ItemsResponse;
import jp.co.spring_boot_test_item.domain.model.Item;
import jp.co.spring_boot_test_item.domain.service.ItemService;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
@GetMapping("/item/{itemId}")
public ResponseEntity<ItemResponse> getItemsByGet(@PathVariable int itemId){
Item item = itemService.findByItemId(itemId);
ItemResponse itemResponse = ItemResponse.builder().item(item).build();
return new ResponseEntity<>(itemResponse, HttpStatus.OK);
}
@PostMapping("/items")
public ResponseEntity<ItemsResponse> getItemsByPost(@Validated @RequestBody ItemPostRequest request){
List<Item> items = itemService.findByCategoryId(request.getCategoryId());
ItemsResponse itemResponse = ItemsResponse.builder().items(items).build();
return new ResponseEntity<>(itemResponse, HttpStatus.OK);
}
}
本次测试的目标是带有@GetMapping和@PostMapping注解的两个方法。
2. 请求(Request)类
package jp.co.spring_boot_test_item.app.request;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ItemPostRequest {
private Integer categoryId;
}
这个方法用于获取使用@PostMapping注解修饰的Controller类中的RequestBody内容。
3. Response 类
package jp.co.spring_boot_test_item.app.response;
import java.util.List;
import jp.co.spring_boot_test_item.domain.model.Item;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class ItemResponse {
// Item返却用
private Item item;
// List<Item>返却用
private List<Item> items;
}
为了在Controller类的响应时进行格式调整时使用。
4. 服务类 (Service class)
package jp.co.spring_boot_test_item.domain.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jp.co.spring_boot_test_item.domain.model.Item;
import jp.co.spring_boot_test_item.domain.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
@Transactional(readOnly = true)
public Item findByItemId(int itemId) {
return itemRepository.findByItemId(itemId);
}
@Transactional(readOnly = true)
public List<Item> findByCategoryId(int categoryId) {
return itemRepository.findByCategoryId(categoryId);
}
}
这是一个Service类。
仅进行数据读取操作,我们将其标记为@Transactional(readOnly=true)(默认值为false)。
通过这样做,即使尝试进行数据操作(例如注册、更新、删除),也不会执行任何处理,也不会发生错误。
(实际上我们根本没有这样的描述,但是…)
5. 商品仓库.java
package jp.co.spring_boot_test_item.domain.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import jp.co.test_item.domain.model.Item;
@Repository
public interface ItemRepository extends JpaRepository<Item, Integer>{
// 「select * from items where item_id = itemId(引数としてわたってくる変数);」と同義
public Item findByItemId(int itemId);
// 「select * from items where category_id = categoryId(引数としてわたってくる変数);」と同義
public List<Item> findByCategoryId(int categoryId);
}
这是一个Repository类。
通过遵循Spring Data JPA的命名规则来定义方法,它能够自动生成查询。
6. 实体类
package jp.co.spring_boot_test_item.domain.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
/**
* The persistent class for the items database table.
*
*/
@Entity
@Table(name="items")
@NamedQuery(name="Item.findAll", query="SELECT i FROM Item i")
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="item_id")
private int itemId;
@Column(name="category_id")
private int categoryId;
@Column(name="item_explanation")
private String itemExplanation;
@Column(name="item_name")
private String itemName;
public Item() {
}
public int getItemId() {
return this.itemId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public int getCategoryId() {
return this.categoryId;
}
public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
}
public String getItemExplanation() {
return this.itemExplanation;
}
public void setItemExplanation(String itemExplanation) {
this.itemExplanation = itemExplanation;
}
public String getItemName() {
return this.itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
}
这是一个实体类。
这次我们使用Hibernate ORM基于数据库表定义来生成它。
测试类的实现
1. 项目控制器测试.java
package jp.co.spring_boot_test_item.app.controller;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.RestController;
import jp.co.spring_boot_test_item.domain.model.Item;
import jp.co.spring_boot_test_item.domain.service.ItemService;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class ItemControllerTest {
MockMvc mockMvc;
@Mock // モックオブジェクトとして使用することを宣言
private ItemService itemService;
@InjectMocks // モックオブジェクトの注入
private ItemController itemController;
@BeforeEach // テストメソッド(@Testをつけたメソッド)実行前に都度実施
public void initmocks() {
MockitoAnnotations.initMocks(this); // アノテーションの有効化
mockMvc = MockMvcBuilders.standaloneSetup(itemController).build(); // MockMvcのセットアップ
}
@Test
public void test_getItemsByGet() throws Exception {
when(itemService.findByItemId(1)).thenReturn(getItemIdOfItemId1()); // itemService.findByItemId(1)実行時にgetItemIdOfItemId1()の結果が返ることを定義
this.mockMvc.perform(get("/item/{itemId}", 1)) // @GetMapping("/item/{itemId}")のメソッドの実行と結果確認
.andExpect(status().isOk()) // 以下、結果確認
.andExpect(jsonPath("$.item.itemId").value(1))
.andExpect(jsonPath("$.item.itemName").value("ふしぎなアメ"))
.andExpect(jsonPath("$.item.itemExplanation").value("レベル1アップします。"))
.andExpect(jsonPath("$.item.categoryId").value(1));
}
@Test
public void test_getItemsByPost() throws Exception {
when(itemService.findByCategoryId(1)).thenReturn(getItemIdOfCategory1()); // findByCategoryId(1)実行時にgetItemIdOfCategory1()の結果が返ることを定義
this.mockMvc.perform(post("/items").contentType(MediaType.APPLICATION_JSON).content("{\"categoryId\":\"1\"}")) // RequestBodyの要素の型と値を設定
.andExpect(jsonPath("$.items", hasSize(2))) // 以下、結果確認
.andExpect(status().isOk())
.andExpect(jsonPath("$.items[0].itemId").value(1))
.andExpect(jsonPath("$.items[0].itemName").value("ふしぎなアメ"))
.andExpect(jsonPath("$.items[0].itemExplanation").value("レベル1アップします。"))
.andExpect(jsonPath("$.items[0].categoryId").value(1))
.andExpect(jsonPath("$.items[1].itemId").value(2))
.andExpect(jsonPath("$.items[1].itemName").value("オボンのみ"))
.andExpect(jsonPath("$.items[1].itemExplanation").value("HPをすこしだけかいふくする。"))
.andExpect(jsonPath("$.items[1].categoryId").value(1));
}
public Item getItemIdOfItemId1() {
Item item = new Item();
item.setItemId(1);
item.setItemName("ふしぎなアメ");
item.setItemExplanation("レベル1アップします。");
item.setCategoryId(1);
return item;
}
public List<Item> getItemIdOfCategory1() {
List<Item> items = new ArrayList<>();
Item item1 = new Item();
item1.setItemId(1);
item1.setItemName("ふしぎなアメ");
item1.setItemExplanation("レベル1アップします。");
item1.setCategoryId(1);
Item item2 = new Item();
item2.setItemId(2);
item2.setItemName("オボンのみ");
item2.setItemExplanation("HPをすこしだけかいふくする。");
item2.setCategoryId(1);
items.add(item1);
items.add(item2);
return items;
}
}
以下是Controller类的测试。
由于使用了@RestController注解,所以重点是以JSON格式进行交互。
测试代码本身也需要与相应的内容相匹配。
2. ItemServiceTest.java文件
package jp.co.spring_boot_test_item.domain.service;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import jp.co.spring_boot_test_item.domain.model.Item;
import jp.co.spring_boot_test_item.domain.repository.ItemRepository;
public class ItemServiceTest {
@Mock // モックオブジェクトとして使用することを宣言
private ItemRepository itemRepository;
@InjectMocks // モックオブジェクトの注入
private ItemService itemService;
@BeforeEach // テストメソッド(@Testをつけたメソッド)実行前に都度実施
public void initmocks() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test_findByItemId() {
when(itemRepository.findByItemId(100)).thenReturn(getItemId100()); // itemRepository.findByItemId(100)実行時にgetItemId100()の結果が返ることを定義
Item item = itemRepository.findByItemId(100);
// 以下、結果確認
assertThat(item.getItemId(), is(100));
assertThat(item.getItemName(), is("超ふしぎなアメ"));
assertThat(item.getItemExplanation(), is("レベル100アップします。"));
assertThat(item.getCategoryId(), is(1));
}
@Test
public void test_findByCategoryId() {
when(itemRepository.findByCategoryId(10)).thenReturn(getItemIdOfCategory10()); // itemRepository.findByCategoryId(10)実行時にgetItemIdOfCategory10()の結果が返ることを定義
List<Item> items = itemRepository.findByCategoryId(10);
// 以下、結果確認
assertThat(items.get(0).getItemId(), is(1));
assertThat(items.get(0).getItemName(), is("ふしぎなアメ"));
assertThat(items.get(0).getItemExplanation(), is("レベル1アップします。"));
assertThat(items.get(0).getCategoryId(), is(1));
assertThat(items.get(1).getItemId(), is(2));
assertThat(items.get(1).getItemName(), is("オボンのみ"));
assertThat(items.get(1).getItemExplanation(), is("HPをすこしだけかいふくする。"));
assertThat(items.get(1).getCategoryId(), is(1));
}
public Item getItemId100() {
Item item = new Item();
item.setItemId(100);
item.setItemName("超ふしぎなアメ");
item.setItemExplanation("レベル100アップします。");
item.setCategoryId(1);
return item;
}
public List<Item> getItemIdOfCategory10(){
List<Item> items = new ArrayList<>();
Item item1 = new Item();
item1.setItemId(1);
item1.setItemName("ふしぎなアメ");
item1.setItemExplanation("レベル1アップします。");
item1.setCategoryId(1);
Item item2 = new Item();
item2.setItemId(2);
item2.setItemName("オボンのみ");
item2.setItemExplanation("HPをすこしだけかいふくする。");
item2.setCategoryId(1);
items.add(item1);
items.add(item2);
return items;
}
}
在Controller类的测试中也使用了Mockito,使用Mockito来定义下层的Repository类的返回值是重点。
3. ItemRepositoryTest.java测试文件
package jp.co.spring_boot_test_item.domain.repository;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.transaction.annotation.Transactional;
import jp.co.spring_boot_test_item.domain.model.Item;
@SpringBootTest
@Transactional
public class ItemRepositoryTest {
@Autowired
JdbcTemplate jdbctemplate;
@Autowired
ItemRepository itemRepository;
String item_id_str = null;
Integer item_id_num = null;
private File file = null;
private FileOutputStream out;
private FileInputStream in;
File tempDir = new File("src/test/java/jp/co/spring_boot_test_item/domain/repository");
@BeforeTransaction // 各@Transactional実行前に都度実施
public void initdb() throws Exception{
Connection connection = jdbctemplate.getDataSource().getConnection();
IDatabaseConnection dbconnection = new DatabaseConnection(connection);
try {
// AUTO_INCREMENTの現在値を取得
String sql = "SHOW TABLE STATUS where name = 'items'";
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet result = statement.executeQuery();
while (result.next()) {
item_id_str = result.getString("Auto_increment");
}
// itemsテーブルのバックアップを取得
QueryDataSet partialDataSet = new QueryDataSet(dbconnection);
partialDataSet.addTable("items");
file=File.createTempFile("items",".xml", tempDir);
out = new FileOutputStream(file);
FlatXmlDataSet.write(partialDataSet, out);
out.flush();
out.close();
// DBの値を削除し、テストデータを投入する(ExcelファイルのパスはItemRepositoryTest.javaと同階層)
IDataSet dataset = new XlsDataSet(new File("src/test/java/jp/co/spring_boot_test_item/domain/repository/ItemRepositoryTest.xlsx"));
DatabaseOperation.CLEAN_INSERT.execute(dbconnection, dataset);
}catch(Exception e) {
e.printStackTrace();
}finally {
connection.close();
dbconnection.close();
}
}
@AfterTransaction // 各@Transactional実行後に都度実施
public void teardown() throws Exception{
Connection connection = jdbctemplate.getDataSource().getConnection();
IDatabaseConnection dbconnection = new DatabaseConnection(connection);
try {
// テストデータを削除し、バックアップファイルの内容を戻す
in = new FileInputStream(file);
IDataSet dataSet= new FlatXmlDataSetBuilder().build(in);
DatabaseOperation.CLEAN_INSERT.execute(dbconnection,dataSet);
// AUTO_INCREMENTをテスト実行前の値に戻す
String sql = new StringBuilder("ALTER TABLE items AUTO_INCREMENT=").append(item_id_str).toString();
PreparedStatement statement = connection.prepareStatement(sql);
statement.executeUpdate();
}catch(Exception e) {
e.printStackTrace();
}finally {
connection.close();
dbconnection.close();
file.deleteOnExit(); // DBバックアップファイルの削除
}
}
@Test
public void test_findByItemId() {
Item expectedItem = new Item();
Item actualItem = itemRepository.findByItemId(1);
assertThat(actualItem.getItemId(), is(1));
assertThat(actualItem.getItemName(), is("ふしぎなアメ"));
assertThat(actualItem.getItemExplanation(), is("レベル1アップします。"));
assertThat(actualItem.getCategoryId(), is(1));
}
@Test
public void test_findByCategoryId() {
Item item1 = new Item();
item1.setItemId(1);
item1.setItemName("ふしぎなアメ");
;
item1.setItemExplanation("レベル1アップします。");
item1.setCategoryId(1);
Item item2 = new Item();
item2.setItemId(4);
item2.setItemName("オボンのみ");
item2.setItemExplanation("HPをすこしだけかいふくする。");
item2.setCategoryId(1);
List<Item> expectedItems = new ArrayList<Item>();
expectedItems.add(item1);
expectedItems.add(item2);
List<Item> actualItems = itemRepository.findByCategoryId(1);
assertThat(actualItems, hasSize(2));
for (int i = 0; i < actualItems.size(); i++) {
for (int j = 0; j < expectedItems.size(); j++) {
if (actualItems.get(i).getItemId() == expectedItems.get(j).getItemId()) {
assertThat(actualItems.get(i).getItemName(), is(expectedItems.get(j).getItemName()));
assertThat(actualItems.get(i).getItemExplanation(), is(expectedItems.get(j).getItemExplanation()));
assertThat(actualItems.get(i).getCategoryId(), is(expectedItems.get(j).getCategoryId()));
break;
}
}
}
}
@Test
public void test_save() throws Exception{
// 更新
Item Item1 = new Item();
Item1.setItemId(1);
Item1.setItemName("ふつうのアメ");
Item1.setItemExplanation("20kcal摂取できます。");
Item1.setCategoryId(3);
itemRepository.save(Item1);
// 追加
Item item2 = new Item();
item2.setItemName("ペロペロキャンディー");
item2.setItemExplanation("子どもにあげると喜ばれます(たぶん)。");
item2.setCategoryId(3);
itemRepository.save(item2);
// DBの値の確認(更新分)
Item dbItem1 = itemRepository.findByItemId(1);
assertThat(dbItem1.getItemId(), is(1));
assertThat(dbItem1.getItemName(), is("ふつうのアメ"));
assertThat(dbItem1.getItemExplanation(), is("20kcal摂取できます。"));
assertThat(dbItem1.getCategoryId(), is(3));
// DBの値の確認(追加分)
item_id_num = Integer.valueOf(item_id_str);
Item dbItem2 = itemRepository.findByItemId(item_id_num);
assertThat(dbItem2.getItemId(), is(item_id_num));
assertThat(dbItem2.getItemName(), is("ペロペロキャンディー"));
assertThat(dbItem2.getItemExplanation(), is("子どもにあげると喜ばれます(たぶん)。"));
assertThat(dbItem2.getCategoryId(), is(3));
}
}
这是一个 Repository 类的测试。
重点有以下两个。
1. 替代使用 @Mock,而是在类上添加 @SpringBootTest 注解。
通过 DBUnit 的功能,在测试方法执行前后,会进行测试数据和数据库数据的互换。
2. 由于是使用 @Transactional 进行的测试,所以在测试执行前后,使用 @BeforeTransaction/@AfterTransaction,而不是 @BeforeEach/@AfterEach(否则在特定的 CUD 类型操作时,可能会出现事务锁的可能性)。
这次,我在上一层没有使用,但是我也写了插入和更新(insert, update)相关的代码,并在测试后加入了将数据库的AUTO_INCREMENT设置回之前状态的描述。
项目仓库测试.xlsx
这是用于Repository类的测试数据。
这次,我们把它放在ItemRepositoryTest.java的同级目录下来使用。
关键是:
1. 在第一行上写下列名
2. 工作表名应与表名相同(本次使用”items”)
补充说明
-
- 在Controller类中,我们使用的是@RestController,而不是带有页面转换的@Controller,它主要用于WebAPI。
- 由于我们在这次重点关注各个类的写法,所以省略了异常和错误测试。
本文更新纪录
-
- 2020年6月13日:
-
- 在GitHub上添加了代码 spring-boot-test-sample。
2020年6月15日:增加了有关实体类和服务类的事务控制的描述。
请参考我以下所提供的选项。
■JUnit (Java单元测试框架)
JUnit的概念全面。
or
JUnit的范围很广。
-
- JUnit5 | junit.org
- JUnit5ユーザーガイド
应用程序.yml文件的配置
-
- Spring Boot 1.5→2.1 に移行したときのお話 | Qiita
- Spring Boot 2.1 でテスト時 @Bean を挿げ替えたかった | Qiita
build.gradle文件的配置
-
- Testing in Java & JVM projects | Gradle6.4.1
- Gradleプロジェクトでspring-boot-starter-testにJUnit5を導入するときの競合を解決する | Qiita
进行标注
-
- JUnit 4からJUnit 5に移行する | Qiita
-
- Eclipse を使って JUnit5 を導入したときのメモ | Qiita
-
- テスト | Spring
- springbootのテストについて-備忘録- | 忘れっぽいエンジニアの備忘録
断言
-
- JUnit4とJUnit5のアサーション | DMCA
Class Assertions | junit.org
→ JUnit5
Class Assert | junit.org
→ JUnit4
考试实施
-
- 10.2.2. レイヤごとのテスト実装 | TERASOLUNA
-
- Spring Bootでテストを書くときのやりかたまとめ | Qiita
-
- Springで外部APIをリクエストする場合のテスト | 壁にでも話してろ
-
- Testing your REST controllers and clients with Spring | DIMITR.IM
- Spring Test – How to test a JSON Array in jsonPath | Mkyong.com
■Mockito 模拟器
莫基托的一般使用
-
- mockito.org
-
- 【Java】Mockitoの飲み方(入門) | Orizuru
- Spring Bootでmockitoを使ってテストする | 株式会社CONFRAGE ITソリューション事業部
MockMvc
模拟MVC (MockMvc)
-
- 10.2.4.2. MockMvc | TERASOLUNA
- Spring Boot MockMvc not working test with @PathVariable [duplicate] | stack overflow
■ DBUnit 数据库单元测试框架
DBUnit常规
-
- DBUnit | TECHSCORE
- 【超初心者向け】DBUnit超入門 | Qiita
其他
春天
- Springの@Transactional(readOnly=true)で読み取り専用のトランザクションにする | 株式会社CONFRAGE ITソリューション事業部