idea中一般使用JUnit進行單元測試?
基本使用
我們可以在idea的test文件夾下的XXXXApplicationTests內進行單元測試:
可以在@Test標注的方法上寫測試代碼:?
@SpringBootTest
class C0101ApplicationTests {@Testfun contextLoads() {println("Hello World")}}
我們也可以寫多個測試方法:
@SpringBootTest
class C0101ApplicationTests {@Testfun test1() {println("test1")}@Testfun test2() {println("test2")}}
我們也可以在測試類內使用@Autowired注解,如我們可以自動注入寫好的服務:
@Autowired
lateinit var testService: TestService
我們來舉個例子,先創建一個服務:
package com.example.c0101.serviceimport org.springframework.stereotype.Service@Service
class TestService {fun check(username: String, password: String): Boolean{return username == "admin" && password == "123456"}
}
然后在測試類內使用Autowired自動注入服務,并進行測試:
package com.example.c0101import com.example.c0101.service.TestService
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest@SpringBootTest
class C0101ApplicationTests {@Autowiredlateinit var testService: TestService@Testfun test() {println(testService.check("111", "123"))println(testService.check("admin", "123456"))}}
控制臺輸出:
...
false
true
...
測前準備和測后收尾
我們可以用以下注解實現測前準備和測后收尾:
- @BeforeEach:在每一個測試方法執行前執行,其標注的方法可以傳入一個TestInfo類型的參數,為當前測試信息的對象
- @AfterEach:在每一個測試方法執行后執行,其標注的方法可以傳入一個TestInfo類型的參數,為當前測試信息的對象
- @BeforeAll:在所有測試方法執行前只執行一次
- @AfterAll:在所有測試方法執行后只執行一次
另外,@BeforeAll和@AfterAll標注的方法需要為靜態,在kotlin中需要放在companion object的代碼塊下,并用@JvmStatic注解標注
以下代碼展示了這些注解的用法:
package com.example.c0101import com.example.c0101.service.TestService
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInfo
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest@SpringBootTest
class C0101ApplicationTests {@BeforeEachfun beforeEach(info: TestInfo){println("即將進入測試方法:${info.testMethod.get()}")}@AfterEachfun afterEach(info: TestInfo){println("已經離開測試方法:${info.testMethod.get()}")}companion object {@JvmStatic@BeforeAllfun beforeAll(){println("即將進入測試")}@JvmStatic@AfterAllfun afterAll(){println("測試已完成")}}@Testfun test() {println("Hello World")}}
控制臺輸出:
...
即將進入測試
...
即將進入測試方法:public void com.example.c0101.C0101ApplicationTests.test()
Hello World
已經離開測試方法:public void com.example.c0101.C0101ApplicationTests.test()
測試已完成
...
設置測試用例
要想設置測試用例,需要使用@ParameterizedTest注解,該注解可以傳入name參數,可以為測試方法起別名。另外,可以用@ValueSource注解設置參數源:
@ParameterizedTest
@ValueSource(ints = [1, 2, 3, 4, 5])
fun test(num: Int) {println("$num")
}
注意,被@ParameterizedTest注解標注的測試方法就不需要用@Test注解標注了
JUnit會將所有的測試用例都測試一遍,因此這個測試方法會被執行5次:
...
1
2
3
4
5
...
我們也可以用@MethodSource注解設置測試用例,它將會把一個靜態方法的返回值作為測試用例:
companion object{@JvmStaticfun getInt(): Stream<Int>{return Stream.of(1, 2, 3, 4, 5)}
}@ParameterizedTest
@MethodSource("getInt")
fun test(num: Int) {println("$num")
}
注意:這里面的Stream是java.util.stream下的Stream類
使用這種方法,我們就可以傳入多個參數了:
companion object{@JvmStaticfun getProducts(): Stream<Arguments>{return Stream.of(Arguments.of("鼠標", 49.9),Arguments.of("鍵盤", 59.9))}
}@ParameterizedTest
@MethodSource("getProducts")
fun test(name: String, price: Double) {println("$name 賣 $price 元")
}
輸出:
...
鼠標 賣 49.9 元
鍵盤 賣 59.9 元
...
斷言
測試人員可以斷言一件事是真的,如果這件事不是真的,則測試失敗
JUnit提供了Assertions類,用于進行斷言:
@Test
fun test() {Assertions.assertTrue(1 > 2)
}
這段代碼斷言了1>2是真的,如果不是真的(當然不是真的),則測試失敗:

斷言的應用
還記得之前的測試服務的代碼嗎,這個服務在傳入用戶名為admin且密碼為123456后應該返回true,如果返回的不是true,說明這個服務寫錯了;同理,如果傳入的用戶名不是admin或密碼不是123456,則應該返回false,如果返回的不是false,同樣說明這個服務寫錯了。我們可以用斷言來測試這個功能:
@Autowired
lateinit var testService: TestService@Test
fun test() {Assertions.assertTrue(testService.check("admin", "123456"))Assertions.assertTrue(!testService.check("aaa", "123"))
}
可以看到,測試通過,說明check沒有寫錯
模擬Servlet對象
如果要測試controller等需要使用Servlet對象(例如HttpServletRequest)的方法,就需要模擬Servlet對象,我們可以在測試類自動注入以下對象模擬:
@Autowired
lateinit var mockHttpServletRequest: MockHttpServletRequest
@Autowired
lateinit var mockHttpServletResponse: MockHttpServletResponse
@Autowired
lateinit var mockHttpSession: MockHttpSession
這些代碼在idea里可能會報錯,不過沒有關系
另外,在測試contoller時,同樣需要@Autowired:
@Autowired
lateinit var controller: TestController
我們來舉個模擬Servlet的例子:
先創建一個controller:
package com.example.c0101.controllerimport jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import jakarta.servlet.http.HttpSession
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController@RestController
class TestController {@RequestMappingfun index(request: HttpServletRequest, response: HttpServletResponse, session: HttpSession): String{response.status = 404return "Hello World"}}
當訪問主頁時,會設置狀態碼為404,并返回Hello World
接下來編寫測試類:
package com.example.c0101import com.example.c0101.controller.TestController
import com.example.c0101.service.TestService
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.mock.web.MockHttpSession@SpringBootTest
class C0101ApplicationTests {@Autowiredlateinit var controller: TestController@Autowiredlateinit var mockHttpServletRequest: MockHttpServletRequest@Autowiredlateinit var mockHttpServletResponse: MockHttpServletResponse@Autowiredlateinit var mockHttpSession: MockHttpSession@Testfun test() {val res = controller.index(mockHttpServletRequest, mockHttpServletResponse, mockHttpSession)Assertions.assertTrue(mockHttpServletResponse.status == 404)println(res)}}
測試類中,我們斷言了狀態碼一定是404,并輸出了返回結果
控制臺輸出如下:
...
Hello World
...