1.Springboot的測試類庫
SpringBoot 提供了許多實用工具和注解來幫助測試應(yīng)用程序蛙粘,主要包括以下兩個模塊思劳。
- spring-boot-test: 支持測試的核心內(nèi)容。
- spring-boot-test-autoconfigure:支持測試的自動化配置逗宁。
開發(fā)進(jìn)行只要引入spring-boot-starter-test的依賴 就能引入這些SpringBoot測試模塊盯滚,還能引入一些像Junit,AssertJ奸晴,Hamcrest及其他一些有用的類庫,具體如下所示日麸。
- Junit: Java應(yīng)用程序單元測試標(biāo)準(zhǔn)類庫寄啼。
- Spring Test & Spring Boot Test: Spring Boot 應(yīng)用程序功能集成化測試支持。
- AssertJ: 一個輕量級斷言類庫代箭。
- Hamcrest: 一個對象匹配器類庫墩划。
- Mockito: 一個java Mock測試框架。
- JSONassert: 一個用于JSON的斷言庫嗡综。
- JsonPath: 一個Json操作類庫乙帮。
Maven 依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2.創(chuàng)建測試類和測試方法
要讓一個普通類變成一個單元測試類:
- 1.在雷鳴上加入@SpringBootTest 和@RunWith(SpringRunner.class)兩個注解即可。
- 2.在測試方法上加上@Test注解极景。
使用IDEA可以使用快捷鍵ctrl + shift + t
或者選中當(dāng)前類名 使用快捷鍵alt + enter
,向下選擇Create Test
即可進(jìn)入測試類的選項中察净,再次回車,就快速的生成測試類戴陡。
生成的測試類在src/test目錄下塞绿,測試類和源代碼寶明是一致的沟涨。
3.JUnit4
JUnit4中的注解
@BeforeClass
:針對所有測試恤批,只執(zhí)行一次,且必須為static void
@Before
:初始化方法裹赴,執(zhí)行當(dāng)前測試類的每個測試方法后執(zhí)行@Test
:測試方法喜庞,在這里可以測試期望異常和超時時間@After
:釋放資源,執(zhí)行當(dāng)前測試類的每個測試方法后執(zhí)行@AfterClass
:針對所有測試棋返,只執(zhí)行一次延都,且必須為static void
Ignore
:忽略的測試方法@Runwith
:可以更改測試運行器,缺省值org.junit.runner.Runner
一個單元測試類執(zhí)行順序為:
@BeforeClass
-> @Before
-> @Test
-> @After
-> @AfterClass
每一個測試方法的調(diào)用順序為:
@Before
-> @Test
-> @After
3.1超時測試
如果一個測試用例比起指定的毫秒數(shù)要花費更多時間睛竣,那么JUnit將自動將他標(biāo)記為失敗晰房,timeout
參數(shù)和@test
注解一起使用。現(xiàn)在讓我們看看活動中的@Test(timeout)
@Test(timeout = 1000)
public void testTimeout() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
System.out.println("Complete");
}
上面測試會失敗,在一秒后會拋出異常org.junit.runners.model.TestTimeOutException:test timedout after 1000 millseconds
3.2異常測試
你可以測試代碼是否它拋出想要得到的異常殊者。expected參數(shù)和@Test 注釋一起使用∮刖常現(xiàn)在讓我們看看活動中的@Test(expected)
@Test(expected = NullPointerException.class)
public void testNullException() {
throw new NullPointerException();
}
3.3套件測試
public class TaskOneTest {
@Test
public void test(){
System.out.println("task one do");
}
}
public class TaskTwoTest {
@Test
public void test(){
System.out.println("task two do");
}
}
public class TaskThreeTest {
@Test
public void test(){
System.out.println("task three do");
}
}
/*1. 更改測試運行方式為 Suite*/
@RunWith(Suite.class)
/*2. 將測試類傳入進(jìn)來*/
@Suite.SuiteClasses({TaskOneTest.class,TaskTwoTest.class,TaskThreeTest.class})
public class SuitTest {
/*測試套件的入口類知識組織測試類一起進(jìn)行測試,無任何測試方法*/
}
3.4參數(shù)化測試
JUnit4 引入了一個新的功能參數(shù)化測試猖吴。參數(shù)化測試允許開發(fā)人員使用不同的值反復(fù)運行同一個測試摔刁。
創(chuàng)建參數(shù)化測試,遵循以下5個步驟海蔽。
- 用
RunWith(Parameterized.class)
來注釋test類共屈。 - 創(chuàng)建一個由
@Parameters
注釋的公共的靜態(tài)方法,它返回一個對象的集合(數(shù)組)來作為數(shù)據(jù)集合党窜。 - 創(chuàng)建一個公共的構(gòu)造函數(shù)拗引,它接受數(shù)據(jù)集合相同的參數(shù)。
- 為每一列測試數(shù)據(jù)創(chuàng)建一個實例變量幌衣。
- 用實例變量作為測試數(shù)據(jù)的來源來創(chuàng)建你的測試用例寺擂。
/*更改默認(rèn)的測試運行器為RunWith(Parameterized.class)*/
@RunWith(Parameterized.class)
public class ParameterTest {
/*聲明變量存放預(yù)期值和測試數(shù)據(jù)*/
private String firstName;
private String lastName;
/*聲明一個返回值 為Collection的公共靜態(tài)方法,并使用@Parameters進(jìn)行修飾*/
@Parameterized.Parameters
public static List<Object[]> param() {
/*這里給出兩個測試用例*/
return Arrays.asList(new Object[][]{{"Mike","Black"},{"Circle","Smith"}});
}
public ParameterTest (String firstName,String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
/*進(jìn)行測試泼掠,發(fā)現(xiàn)它會將所有的測試用例測試一遍*/
@Test
public void test(){
String name = firstName + " " + lastName;
System.out.println(name);
}
4.Assert
assert常用方法
-
assertEquals("message",A,B)
:判斷對象A和B是否相等僵控,這個判斷比較時調(diào)用了equals()方法。 -
assertSame("message",A,B)
:判斷對象A和B是否相同浅碾,使用的是==操作符影暴。 -
assertTure("message",A)
:判斷A條件是否為真。 -
assertFalse("message",A)
:判斷A條件是否不為真腻豌。 -
assertNotNull("message",A)
:判斷A對象是否不為null -
assertArrayEquals("message",A,B)
: 判斷A數(shù)組與B數(shù)組是否相等家坎。
5.Mockito
什么是mock
在面向?qū)ο蟮某绦蛟O(shè)計中,模擬對象(mock object)是以可控的方式模擬真實對象行為的假對象吝梅。在編程過程中虱疏,通常通過模擬一些輸入數(shù)據(jù),來驗證程序是否達(dá)到預(yù)期結(jié)果苏携。
為什么使用Mock對象
使用模擬對象做瞪,可以模擬復(fù)雜的、真實的對象行為右冻。如果在單元測試中無法使用真實對象装蓬,可采用模擬對象進(jìn)行替代。
在以下情況可以采用模擬對象來替代真實對象:
- 真實對象的行為是不確定的(例如纱扭,當(dāng)前的時間或溫度)牍帚。
- 真實對象很難搭建起來。
- 真實對象的行為很難觸發(fā)(例如乳蛾,網(wǎng)絡(luò)錯誤)暗赶。
- 真實對象速度很難鄙币。
- 真實對象是用戶界面,或包括用戶界面在內(nèi)蹂随。
- 真實的對象使用了回調(diào)機制爱榔。
- 真實對象可能還不存在。
- 真實對象可能包含不能用作測試的信息和方法糙及。
使用Mockito一般分為三個步驟:
- 1.模擬測試類所需的外部依賴
- 2.執(zhí)行測試代碼
- 3.判斷執(zhí)行結(jié)果是否達(dá)到預(yù)期
Mockito
JUnit和SpringTest基本上可以滿足絕大多數(shù)單元測試详幽,但是由于現(xiàn)在系統(tǒng)越來越復(fù)雜,相互之間依賴越來越多浸锨。特別是微服務(wù)化以后的系統(tǒng)唇聘,往往一個模塊的代碼需要依賴幾個其他模塊的東西。因此柱搜,在做單元測試的時候迟郎,往往很難構(gòu)造出需要的依賴。一個單元測試聪蘸,我們只關(guān)心一個小的功能宪肖,但是為了這個小的功能能跑起來,可能需要依賴一堆其他的東西健爬,這就導(dǎo)致了單元測試無法進(jìn)行控乾。所以我們就需要在測試過程中引入mock
測試。
所謂的Mock
測試就是在測試過程中娜遵,對于一些不容易構(gòu)造的蜕衡、或者和這次單元測試無關(guān)但是上下文又有依賴的對象,用一個虛擬的對象(Mock對象)來模擬设拟,以便單元測試能夠進(jìn)行慨仿。
比如有一段代碼的依賴為:
當(dāng)我們要進(jìn)行單元測試的時候,就需要給A
注入B
和C
但是C
又依賴了D
纳胧,D
又依賴了E
镰吆。這就導(dǎo)致了A
的單元測試很難進(jìn)行。
但是當(dāng)我們使用Mock來進(jìn)行模擬對象后跑慕,我們就可以把這種依賴解耦万皿,只關(guān)心A本身的測試,它所依賴的B和C相赁,全部使用Mock出來的對象相寇,并且給MockB
和MockC
指定一個明確的行為慰于。就像這樣:
因此钮科,當(dāng)我們使用Mock后,對于那些難以構(gòu)建的對象婆赠,就變成了個模擬對象绵脯,只需要提前的做Stubbing
(樁)即可佳励。所謂的做樁數(shù)據(jù),也就是告訴Mock對象蛆挫,當(dāng)與之交互時執(zhí)行何種行為過程赃承。比如當(dāng)調(diào)用B對象的b()方法時,我們期望返回一個true,這就是一個設(shè)置樁數(shù)據(jù)的預(yù)期悴侵。
mockito 使用詳解
現(xiàn)有如下代碼:
實體類
@Entity
@Data
@NoArgsConstructor
public class User implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true,nullable = false,length = 50)
private String username;
private String password;
@CreationTimestamp
private Date createDate;
public User(Long id,String username) {
this.id = id;
this.username = username;
}
}
Repository
public interface IUserRepository extends JpaRepository<User,Long>{
boolean updateUser(User user);
}
Service
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserServiceImpl implements IUserService {
private final IUserRepository userRepository;
@Override
public User findOne(Long id) {
return userRepository.getOne(id);
}
@Override
public boolean updateUsername(Long id, String username) {
User user = findOne(id);
if(user == null) {
return false;
}
user.setUsername(username);
return userRepository.updateUser(user);
}
}
Test
public class IUserServiceTest {
private IUserService userService;
// @Mock
private IUserRepository userRepository;
@Before
public void setUp() throws Exception {
/*對所有注解了@Mock的對象進(jìn)行模擬*/
// MockitoAnnotations.initMocks(this);
/*如果不使用注解瞧剖,可以對單個對象進(jìn)行mock*/
userRepository = Mockito.mock(IUserRepository.class);
/*構(gòu)造測試對象*/
userService = new UserServiceImpl(userRepository);
/*打樁,構(gòu)建當(dāng)userRepository getOne函數(shù)執(zhí)行參數(shù)為1的時候可免,設(shè)置返回的結(jié)果User*/
Mockito.when(userRepository.getOne(1L)).thenReturn(new User(1L,"jack"));
/*打樁抓于,構(gòu)建當(dāng)userRepository getOne函數(shù)執(zhí)行參數(shù)為1的時候,設(shè)置返回的結(jié)果null*/
Mockito.when(userRepository.getOne(2L)).thenReturn(null);
/*打樁浇借,構(gòu)建當(dāng)userRepository getOne函數(shù)執(zhí)行參數(shù)為1的時候捉撮,設(shè)置拋出異常*/
Mockito.when(userRepository.getOne(3L)).thenThrow(new IllegalArgumentException("the id is not support"));
/*打樁,構(gòu)建當(dāng)userRepository updateUser執(zhí)行任意User類型的參數(shù)妇垢,返回的結(jié)果都是true*/
Mockito.when(userRepository.updateUser(Mockito.any(User.class))).thenReturn(true);
/*打樁巾遭,給void方法 */
Mockito.doAnswer(invocation -> {
System.out.println("進(jìn)入Mock");
return null;
}).when(userRepository).addUser(Mockito.any());
/*模擬方法設(shè)置返回期望值*/
List spy = Mockito.spy(new LinkedList<>());
/*這里會拋出IndexOutOfBoundsException*/
// Mockito.when(spy.get(0)).thenReturn("foo");
/*所以要使用下面代碼*/
Mockito.doReturn("foo").when(spy).get(0);
}
@Test
public void testUpdateUsernameSuccess() throws Exception {
Long userId = 1L;
String newUsername = "new Jack";
/*測試service方法*/
boolean updated = userService.updateUsername(userId,newUsername);
/*檢查結(jié)果*/
Assert.assertThat(updated, Matchers.is(true));
/*Mock對象一旦創(chuàng)建,就會自動記錄自己的交互行為闯估。通過verify(mock).someMethod()方法灼舍,來驗證方法是否被調(diào)用。*/
/*驗證調(diào)用上面的service方法后是否 userRepositroy.getOne(1L)調(diào)用過涨薪。*/
Mockito.verify(userRepository).getOne(userId);
/*updateUsername 函數(shù)中我們調(diào)用了已經(jīng)打樁了的其他的函數(shù)片仿,現(xiàn)在我們來驗證進(jìn)入其他函數(shù)中的參數(shù)*/
/*構(gòu)造參數(shù)捕獲器,用于捕獲方法參數(shù)進(jìn)行驗證*/
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
/*驗證updateUser方法是否唄調(diào)用過尤辱,并且捕獲入?yún)?/
Mockito.verify(userRepository).updateUser(userCaptor.capture());
/*獲取參數(shù)updateUser*/
User updateUser = userCaptor.getValue();
/*驗證入?yún)⑹欠袷穷A(yù)期的*/
Assert.assertThat(updateUser.getUsername(),Matchers.is(newUsername));
/*保證這個測試用例中所有被Mock的對象的相關(guān)方法都已經(jīng)被Verify過了*/
Mockito.verifyNoMoreInteractions(userRepository);
/*如果有一個交互沒有被verify砂豌,則會報錯
org.mockito.exceptions.verification.NoInteractionsWanted:
No interactions wanted here:
-> at com.wuwii.service.IUserServiceTest.testUpdateUsernameSuccess(IUserServiceTest.java:74)
But found this interaction on mock 'iUserRepository':
-> at com.wuwii.service.impl.UserServiceImpl.findOne(UserServiceImpl.java:21)
****/
}
// @Test
public void testUpdateUsernameFailed() throws Exception {
Long userId = 2L;
String newUsername = "new Jack";
/*沒有經(jīng)過mock的updateUser方法,它的返回值是false*/
boolean updated = userService.updateUsername(userId,newUsername);
Assert.assertThat(updated,Matchers.is(true));
/*驗證userRepository的getOne(2L)這個方法是否被調(diào)用過(這個是被測試過的光督,此步驟通過)*/
Mockito.verify(userRepository).getOne(2L);
/*驗證userRepository的updateUser(null)這個方法是否被調(diào)用過(這個方法是沒有被調(diào)用過的)*/
Mockito.verify(userRepository).updateUser(null);
Mockito.verifyNoMoreInteractions(userRepository);
}
創(chuàng)建Mock對象
我們需要對userService進(jìn)行測試阳距,就需要模擬userRepository對象
我們在setUp()
方法中,模擬對象并打樁。
模擬對象有兩種方式:
- 1.對注解
@Mock
的對象進(jìn)行模擬MockitoAnnotations.initMocks(this)
- 2.對單個對象手動Mock:
userRepositroy = Mockito.mock(IUserRepositroy.class)
數(shù)據(jù)打樁
數(shù)據(jù)打樁结借,方法非常多筐摘,主要分下面幾種:
1.最基本的用法就是調(diào)用
when
以及thenReturn
方法了。它的作用就是指定當(dāng)我們調(diào)用被代理的對象的某一個方法以及參數(shù)的時候船老,返回什么值咖熟。2.提供參數(shù)匹配器,靈活匹配參數(shù)柳畔。
any()
馍管、any(Class<T> type)
、anyBoolean()
薪韩、anyByte()
anyChar()
确沸、anyInt()
捌锭、anyLong()
等等,它支持復(fù)雜的過濾罗捎,可以使用正則Mockito.matches(".*User$")
观谦,開頭結(jié)尾驗證endsWith(String suffix)
、startsWith(String prefix)
桨菜,判空驗證isNotNull()
豁状、isNull()
。也還可以使用argThat(ArgumentMatchermatcher)
,如:ArgumentMatcher
只有一個方法boolean matches(T argument);
傳入入?yún)⒌沟茫祷匾粋€boolean表示是否匹配替蔬。Mockito.argThat(argument -> argument.getUsername.length() > 6)
3.Mockito還提供了了兩個表示行為的方法:
thenAnswer(Answer<?> answer);
、thenCallRealMethod();
分別表示自定義處理調(diào)用后的行為屎暇,以及調(diào)用真實的方法承桥。這兩個方法在有些測試用例中還是很有用的。4.對于同一個方法根悼,Mockito可以是順序與次數(shù)關(guān)連的凶异。也就是說可以實現(xiàn)同一個方法 ,第一次調(diào)用返回一個值挤巡,第二次調(diào)用返回一個值剩彬,甚至第三次調(diào)用拋出異常等等。只需要連續(xù)的調(diào)用
thenXXXX
即可矿卑。如果為一個返回Void的方法設(shè)置樁數(shù)據(jù)喉恋。上面的方法都是表示的是有返回值的方法,而由于一個方法沒有返回值母廷,因此我們不能調(diào)用
when
方法轻黑,比如:doAnswer(Answer answer)
、doNothing()
琴昆、doReturn(Object toBeReturned)
氓鄙、doThrow(Class<? extends Throwable> toBeThrown)
、doCallRealMethod()
业舍。它們使用方法其實和上面thenXXXX
是一樣的.
/*打樁抖拦,給void方法 */
Mockito.doAnswer(invocation -> {
System.out.println("進(jìn)入Mock");
return null;
}).when(userRepository).addUser(Mockito.any());
/*模擬方法設(shè)置返回期望值*/
List spy = Mockito.spy(new LinkedList<>());
/*這里會拋出IndexOutOfBoundsException*/
// Mockito.when(spy.get(0)).thenReturn("foo");
/*所以要使用下面代碼*/
Mockito.doReturn("foo").when(spy).get(0);
驗證測試方法的結(jié)果
使用斷言來檢查結(jié)果。
驗證Mock對象的調(diào)用
其實舷暮,在這里我們?nèi)绻皇球炞C方法結(jié)果的正確的話态罪,就非常簡單,但是在復(fù)雜的方法調(diào)用堆棧中下面,往往可能出現(xiàn)結(jié)果正確复颈,但是過程不正確的情況。比如updateUsername
方法返回false有兩種可能诸狭,一直可能是用戶沒有找到券膀,還有一種可能就是userRepository.updateUser(userPO)
返回false君纫。因此如果我們只使用Assert.assertFalse(updated);
來驗證結(jié)果驯遇,可能就會忽略某些錯誤芹彬。
因此我們在測試中還需要驗證指定的方法userRepository.getOne(userId);
是否運行過,而且我們還是用了參數(shù)捕獲器叉庐,抓取中間的方法參數(shù)舒帮。用來驗證。
提供了verify(T mock,VerificationMode mode)
方法陡叠。VerificationMode有很多作用玩郊。
/*驗證指定方法 get(3) 沒有被調(diào)用*/
verify(mock,never()).get(3);
verifyZeroInteractions
和verifyNoMoreInteractions
驗證所有mock的方法是否都調(diào)用過了。
MockMvc
MockMvc是由spring-test包提供枉阵,實現(xiàn)了對Http請求的模擬译红,能夠直接使用網(wǎng)絡(luò)的形勢,轉(zhuǎn)換到Controller的調(diào)用兴溜,是的測試速度快侦厚,不依賴網(wǎng)絡(luò)環(huán)境。同時提供了一套驗證的工具拙徽,結(jié)果的驗證十分方便刨沦。
接口MockMvcBuilder,一共一個唯一的build方法膘怕,用來構(gòu)造MockMvc想诅。主要有兩個實現(xiàn):StandaloneMockMvcBuilder
和DefaultMockMvcBuilder
,分別對應(yīng)兩種測試方式,即獨立安裝和繼承web環(huán)境測試(并不會集成真正的web環(huán)境岛心,而是通過相應(yīng)的Mock API進(jìn)行模擬測試来破,無需啟動服務(wù))。MockMvcBuilders提供了對應(yīng)的創(chuàng)建方法standaloneSetup
方法和webAppContextSetup
方法,在使用時直接調(diào)用即可忘古。
private MockMvc mockMvc;
@Autowire
private WebApplicationContext webApplicationContext;
@Before
public void setup() {
/*實例化方式一*/
mockMvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
/*實例化方式二*/
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
單元測試方法:
@Test
public void testHello() throws Exception {
/*
1.mockMvc.perform 執(zhí)行一個請求
2.MockMvcRequestBuilders.get("XXX")構(gòu)造一個請求
3.ResultActions.param()
4.ResultActions.accept()
5.ResultActions.andExpect
6.ResultActions.andDo 添加一個結(jié)果處理器讳癌,表示要對結(jié)果做點什么事情。
7.ResultActions.andReturn 表示執(zhí)行完成后存皂,返回響應(yīng)的結(jié)果晌坤。
*/
mockMvc.perform(MockMvcRequestBuilders.get("/mock-mvc/test-get")
/*設(shè)置返回類型為utf-8,否則默認(rèn)為ISO-8859-1*/
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.param("name","tom"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("hello"))
.andDo(MockMvcResultHandlers.print());
}
整個過程如下
1.準(zhǔn)備測試環(huán)境
2.通過MockMvc執(zhí)行請求
3.添加驗證斷言
4.添加結(jié)果處理器
5.得到MvcResult進(jìn)行分自定義斷言/進(jìn)行下一步異步請求
6.卸載測試環(huán)境
注意事項:如果使用DefaultMockMvcBuilder進(jìn)行MockMvc實例化時需在SpringBoot啟動類上添加組件掃描的package的指定,否則會出現(xiàn)404
@ComponentScan(basePackages = "com.creators")
相關(guān)API
RequestBuilder提供了一個方法buildRequest(ServletContext servletContext)用于構(gòu)建MockHttpServletRequest旦袋;其中有兩個子類MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(文件上傳使用) 骤菠。
MockMvcRequestBuilders提供get、post等多種方法用來實例化RequestBuilder疤孕。
ResultActions,MockMvc.perform(RequestBuilder requestBuilder)的返回值商乎,提供三種能力:andExpect
添加斷言判斷結(jié)果是否達(dá)到預(yù)期;andDo
,添加結(jié)果處理器祭阀,比如示例中的打印鹉戚。andReturn
返回驗證成功后的MvcResult,用于自定義驗證/下一步的異步處理鲜戒。
一些常用的測試
測試普通控制器
mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}",1))
/*驗證存儲模型數(shù)據(jù)*/
.andExpect(MockMvcResultMatchers.model().attributeExists("user"))
/*驗證viewName*/
.andExpect(MockMvcResultMatchers.view().name("user/view"))
/*驗證視圖渲染時forward到的jsp*/
.andExpect(MockMvcResultMatchers.forwardedUrl("/WEB-INF/jsp/user/view/jsp"))
/*驗證狀態(tài)碼*/
.andExpect(MockMvcResultMatchers.status().isOk())
/*輸出MvcResult到控制臺*/
.andDo(MockMvcResultHandlers.print());
得到MvcResult自定義驗證
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get( "/user/{id}",1))
.andReturn();
Assert.assertNotNull(result.getModelAndView().getModel().get("user"));
驗證請求參數(shù)綁定到模型數(shù)據(jù)及flash屬性
mockMvc.perform(MockMvcRequestBuilders.post("/user").param("name","wang"))
/*驗證執(zhí)行控制器類*/
.andExpect(MockMvcResultMatchers.handler().handlerType(UserController.class))
/*驗證執(zhí)行控制器方法名*/
.andExpect(MockMvcResultMatchers.handler().methodName("create"))
/*驗證頁面沒有錯誤*/
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
/*驗證存在flash屬性*/
.andExpect(MockMvcResultMatchers.flash().attributeExists("success"))
/*驗證視圖名稱*/
.andExpect(MockMvcResultMatchers.view().name("redirect:/user"));
文件上傳
byte[] bytes = new byte[]{1,2};
mockMvc.perform(MockMvcRequestBuilders.multipart("/user/{id}/icon",1L).file("icon",bytes))
.andExpect(MockMvcResultMatchers.model().attribute("icon",bytes))
.andExpect(MockMvcResultMatchers.view().name("success"));
JSON請求/響應(yīng)驗證
String requestBody = "{\"id\":1,\"name\":\"wang\"}";
mockMvc.perform(MockMvcRequestBuilders.post("/user")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.content(requestBody)
.accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
/*檢查返回JSON數(shù)據(jù)中某個值的內(nèi)容: 請參考http://goessner.net/articles/JsonPath/*/
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value(1));
String errorBody = "{id:1,name:wang}";
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/user")
.contentType(MediaType.APPLICATION_JSON).content(errorBody)
.accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isBadRequest())
.andReturn();
Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(mvcResult.getResolvedException().getClass()));
異步測試
MvcResult mvcResult1 = mockMvc.perform(MockMvcRequestBuilders.get("/user/async?id=1&name=wang"))
.andExpect(MockMvcResultMatchers.request().asyncStarted())
.andExpect(MockMvcResultMatchers.request().asyncResult(CoreMatchers.instanceOf(User.class)))
.andReturn();
mockMvc.perform(MockMvcRequestBuilders.asyncDispatch(mvcResult1))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value(1));
使用MultiValueMap構(gòu)建參數(shù)
MultiValueMap<String,String> params = new LinkedMultiValueMap<>();
params.add("name","wang");
params.add("hobby","sleep");
params.add("hobby","eat");
mockMvc.perform(MockMvcRequestBuilders.post("/user").params(params));
模擬session和cookie
mockMvc.perform(MockMvcRequestBuilders.get("/index").sessionAttr("name", "value"));
mockMvc.perform(MockMvcRequestBuilders.get("/index").cookie(new Cookie("name", "value")));
全局配置
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.defaultRequest(MockMvcRequestBuilders.get("/user/1").requestAttr("default",true))
.alwaysDo(MockMvcResultHandlers.print())
.alwaysExpect(MockMvcResultMatchers.request().attribute("default",true))
.build();
如果是測試Service層代碼 可以在單元測試方法上加上@Transactional注解,在測試完畢后抹凳,數(shù)據(jù)能自動回滾遏餐。
本節(jié)全部源碼