簡介
本篇文章我們將會探討一下怎麼在SpringBoot使用測試,Spring Boot有專門的spring-boot-starter-test,通過使用它可以很方便的在Spring Boot進行測試。
本文將從repository,service, controller,app四個層級來詳細描述測試案例。
添加maven依賴
<code><
dependency
><
groupId
>org.springframework.bootgroupId
><
artifactId
>spring-boot-starter-testartifactId
><
scope
>testscope
>dependency
><
dependency
><
groupId
>com.h2databasegroupId
><
artifactId
>h2artifactId
><
scope
>testscope
>dependency
> /<code>
我們添加spring-boot-starter-test和com.h2database總共兩個依賴。H2數據庫主要是為了測試方便。
Repository測試
本例中,我們使用JPA,首先創建Entity和Repository:
<code>@Entity
@Table
(name ="person"
) public class Employee {@Id
@GeneratedValue
(strategy = GenerationType.AUTO) private Long id;@Size
(min =3
, max =20
) private String name; } /<code>
<code>public
interface
EmployeeRepository
extends
JpaRepository
<Employee
,Long
> {public
EmployeefindByName
(String name)
; } /<code>
測試JPA,我們需要使用@DataJpaTest:
<code>@RunWith
(SpringRunner.class)@DataJpaTest
public class EmployeeRepositoryIntegrationTest {@Autowired
private TestEntityManager entityManager;@Autowired
private EmployeeRepository employeeRepository; } /<code>
@RunWith(SpringRunner.class) 是Junit和Spring Boot test聯繫的橋樑。
@DataJpaTest為persistence layer的測試提供瞭如下標準配置:
- 配置H2作為內存數據庫
- 配置Hibernate, Spring Data, 和 DataSource
- 實現@EntityScan
- 開啟SQL logging
下面是我們的測試代碼:
<code>public
void
whenFindByName_thenReturnEmployee
()
{ Employee alex =new
Employee("alex"
); entityManager.persist(alex); entityManager.flush(); Employee found = employeeRepository.findByName(alex.getName()); assertThat(found.getName()) .isEqualTo(alex.getName()); } /<code>
在測試中,我們使用了TestEntityManager。 TestEntityManager提供了一些通用的對Entity操作的方法。上面的例子中我們使用TestEntityManager向Employee插入了一條數據。
Service測試
在實際的應用程序中,Service通常要使用到Repository。但是在測試中我們可以Mock一個Repository,而不用使用真實的Repository。
先看一下Service:
<code>public
class
EmployeeServiceImpl
implements
EmployeeService
{private
EmployeeRepository employeeRepository;public
EmployeegetEmployeeByName
(String name)
{return
employeeRepository.findByName(name); } } /<code>
我們再看一下怎麼Mock Repository。
<code> (SpringRunner.
class
)public
class
EmployeeServiceImplIntegrationTest
{static
class
EmployeeServiceImplTestContextConfiguration
{public
EmployeeServiceemployeeService
()
{return
new
EmployeeServiceImpl(); } }private
EmployeeService employeeService;private
EmployeeRepository employeeRepository; } /<code>
看下上面的例子,我們首先使用了@TestConfiguration專門用在測試中的配置信息,在@TestConfiguration中,我們實例化了一個EmployeeService Bean,然後在
EmployeeServiceImplIntegrationTest自動注入。
我們還是用了@MockBean,用來Mock一個EmployeeRepository。
我們看下Mock的實現:
<code>public
void
setUp
()
{ Employee alex =new
Employee("alex"
); Mockito.when(employeeRepository.findByName(alex.getName())) .thenReturn(alex); }public
void
whenValidName_thenEmployeeShouldBeFound
()
{ String name ="alex"
; Employee found = employeeService.getEmployeeByName(name); assertThat(found.getName()) .isEqualTo(name); } /<code>
上面的代碼中,我們使用Mockito來Mock要返回的數據,然後在接下來的測試中使用。
測試Controller
和測試Service一樣,Controller使用到了Service:
<code>@RestController
@RequestMapping
("/api"
) public class EmployeeRestController {@Autowired
private EmployeeService employeeService;@GetMapping
("/employees"
) public List getAllEmployees() {return
employeeService
.getAllEmployees
(); } } /<code>
但是在測試的時候,我們並不需要使用真實的Service,我們需要Mock它 。
<code>@RunWith
(SpringRunner.class)@WebMvcTest
(EmployeeRestController.class) public class EmployeeControllerIntegrationTest {@Autowired
private MockMvc mvc;@MockBean
private EmployeeService service; /<code>
為了測試Controller,我們需要使用到@WebMvcTest,他會為Spring MVC 自動配置所需的組件。
通常情況下@WebMvcTest 會和@MockBean一起使用來提供Mock的具體實現。
@WebMvcTest也提供了自動配置的MockMvc,它為測試MVC Controller提供了更加簡單的方式,而不需要啟動完整的HTTP server。
<code>@Test
public
void
givenEmployees_whenGetEmployees_thenReturnJsonArray
() throws Exception { Employee alex =new
Employee("alex"
); List allEmployees = Arrays.asList(alex); given(service.getAllEmployees()).willReturn(allEmployees); mvc.perform(get
("/api/employees"
) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$"
, hasSize(1
))) .andExpect(jsonPath("$[0].name"
,is
(alex.getName()))); } /<code>
given(service.getAllEmployees()).willReturn(allEmployees); 這一行代碼提供了mock的輸出。方面後面的測試使用。
@SpringBootTest的集成測試
上面我們講的都是單元測試,這一節我們講一下集成測試。
<code>@RunWith
(SpringRunner.class)@SpringBootTest
( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = TestApplication.class)@AutoConfigureMockMvc
@TestPropertySource
( locations ="classpath:application-integrationtest.properties"
) public class EmployeeAppIntegrationTest {@Autowired
private MockMvc mvc;@Autowired
private EmployeeRepository repository; } /<code>
集成測試需要使用@SpringBootTest,在@SpringBootTest中可以配置webEnvironment,同時如果我們需要自定義測試屬性文件可以使用@TestPropertySource。
下面是具體的測試代碼:
<code> @After
public
void
resetDb
() { repository.deleteAll(); } @Test
public
void
givenEmployees_whenGetEmployees_thenStatus200
() throws Exception { createTestEmployee("bob"
); createTestEmployee("alex"
); mvc.perform(get
("/api/employees"
).contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$"
, hasSize(greaterThanOrEqualTo(2
)))) .andExpect(jsonPath("$[0].name"
,is
("bob"
))) .andExpect(jsonPath("$[1].name"
,is
("alex"
))); }private
void
createTestEmployee
(String name
) { Employee emp =new
Employee(name); repository.saveAndFlush(emp); } /<code>
歡迎關注我的公眾號:程序那些事,更多精彩等著您!
更多內容請訪問:flydean的博客 flydean.com