寫在前面
一 我們的現狀


-
開發成本
-
維護成本
-
ROI
二 怎麼解決
-
測試資料 (被測資料,和依賴物件) -
測試方法 -
返回值斷言
publicvoid testAddGroup() {
// 資料
BuyerGroupDTO groupDTO = new BuyerGroupDTO();
groupDTO.setGmtCreate(newDate());
groupDTO.setGmtModified(newDate());
groupDTO.setName("中國");
groupDTO.setCustomerId(customerId);
// 方法
Result<Long> result = customerBuyerDomainService.addBuyerGroup(groupDTO);
// 返回值斷言
Assert.assertTrue(result.isSuccess());
Assert.assertNotNull(result.getData());
}
1 測試框架選擇
"racecar", "radar", "able was I ere I saw elba" }) (strings = {
void palindromes(String candidate) {
assertTrue(StringUtils.isPalindrome(candidate));
}
public void test2Test(JSONObject arg) {
Animal animal = JSONObject.parseObject(arg.getString("Animal"),Animal.class);
List<String> stringList = JSONObject.parseArray(arg.getString("List<String>"),String.class);
when(testService.testOther(any(Student.class))).thenReturn(stringArg);
when(testService.testMuti(any(List.class),any(Integer.class))).thenReturn(stringList);
when(testService.getAnimal(any(Integer.class))).thenReturn(animal);
String result = kMPAlgorithm.test2();
//todo verify the result
}
2 mock框架
3 資料庫層
三 Junit5和Mockito
1 使用方法
-
分別單獨引入依賴,推薦引入最新版
<!-- junit5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<!-- mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
<!-- mockito 的junit5介面卡 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.5.0</version>
</dependency>
2 Mockito 主要註解介紹
publicclassA{
private B b; // 完全mock
private C c; // 需要執行方法
// 需要執行真實方法 D d;
publicvoidfunc(){
}
}
classC{
private B b;
publicvoidneedExec(){
}
}
publicclassB{
}
@InjectMocks註解
-
被測試類,這種很容易理解,我們測試這個類,當然也需要向其注入bean。比如上面的A -
被測試類中的,需要執行其真實的方法,但其裡面也要主要bean,也就是上面的C,我們需要測試neeExec方法,但我們不關係B的具體細節。現實中比如事物,併發鎖等。這一類需要Mockito.spy(new C())的形式,不然會報錯
@Mock
@Spy
(MockitoExtension.class)
publicclassATest{
private A a=new A();
private B b;
private D d;
private C c= Mockito.spy(new C());;
publicvoidsetUp()throws Exception {
MockitoAnnotations.openMocks(this);
}
"/com/alibaba/cq/springtest/jcode5test/needMockService/A/func.json"}) (strings = {
publicvoidfuncTest(String str){
JSONObject arg= TestUtils.getTestArg(str);
a.func();
//todo verify the result
}
}
3 Mockito和junit5常見問題
1. mock靜態方法
2. Mockito版本和java版本相容問題
Mockito cannot mock thisclass: xxx
Mockito can only mock non-private & non-final classes.
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.12.6</version>
</dependency>
3. Jupiter-api版本相容問題
Process finished withexit code 255
java.lang.NoSuchMethodError: org.junit.jupiter.api.extension.ExtensionContext.getRequiredTestInstances()Lorg/junit/jupiter/api/extension/TestInstance
四 測試程式碼自動生成
1 業界和集團方案調研
publicclassBaseTest {
protected TestService testService;
public String baseTest() {
return testService.testBase(1); // 4
}
}
publicclassJCode5extendsBaseTest {
publicvoidtestExtend(){
String s = testService.testOther(new Student()); //1
// 呼叫 另一個方法
System.out.println(testBean());
// 呼叫基類方法
baseTest();
}
// 使用testService
public String testBean() {
testService.testMuti(new ArrayList<Integer>() {{add(1);}}, 2); //2
return testService.getStr(12); //3
}
/**
* 測試範型類
*/
publicvoidtestGeneric(Person person) {
//test
list.stream().forEach(a -> {
System.out.println(a);
});
for (int i = 0; i < 2; i++) {
Long aLong = testService.getLong("1213"
, "12323");
System.out.println(aLong);
}
System.out.println(testBean());
}
}
publicclassTestService {
public String testBase(Integer integer) {
return"TestBase";
}
public List<String> testMuti(List<Integer> a, Integer c) {
List<String> res = new ArrayList<>();
res.add(a.toString() + c + "test muti");
return res;
}
public String getStr(Integer integer) {
return"TestService" + getInt();
}
public String testOther(Student student) {
return student.getAge() + "age";
}
}
1、TestMe
@Test
void testTestExtend() {
when(testService.getStr(anyInt())).thenReturn("getStrResponse");
when(testService.testMuti(any(), anyInt())).thenReturn(Arrays.<String>asList("String"));
when(testService.testOther(any())).thenReturn("testOtherResponse");
jCode5.testExtend(Integer.valueOf(0));
}
@Test
void testTestGeneric() {
when(testService.getStr(anyInt())).thenReturn("getStrResponse");
when(testService.getLong(anyString(), anyString())).thenReturn(Long.valueOf(1));
when(testService.testMuti(any(), anyInt())).thenReturn(Arrays.<String>asList("String"));
jCode5.testGeneric(newPerson());
}
2、JunitGenerate
publicvoidtestTestExtend()throws Exception {
//TODO: Test goes here...
}
3、Squaretest
五 打造程式碼自動生成最佳方案
1 外掛安裝

2 外掛使用
-
生成測試程式碼,也就是生成單元測試。 -
生成json資料,通常用來生成測試資料,比如model。用來引數化測試。 -
增加測試方法,隨著業務開發,類可能增加一下功能方法,這個時候相應的可以增加測試方法

1、生成測試類

2、生成的結果—類+json資料
"/com/cq/common/JCode5/testExtend.json"}) (strings = {
publicvoid testExtendTest(String str) {
JSONObject arg= TestUtils.getTestArg(str);
Integer i = arg.getInteger("Integer");
// 識別泛型活著集合類
List<String> stringList = JSONObject.parseArray(arg.getString("List<String>"),String.class);
String stringArg = arg.getString("String");
String stringArg1 = arg.getString("String");
String stringArg0 = arg.getString("String");
// 識別四個方法,包括父類呼叫、其他方法呼叫
when(testService.testBase(any(Integer.class))).thenReturn(stringArg);
when(testService.testMuti(any(List.class),any(Integer.class))).thenReturn(stringList);
when(testService.getStr(any(Integer.class))).thenReturn(stringArg0);
when(testService.testOther(any(Student.class))).thenReturn(stringArg1);
jCode5.testExtend(i);
//todo verify the result
}
{
"Integer":1,
"String":"test",
"List<String>":[
"test"
]
}
3、補充判定語句
注意事項
-
該外掛生成的程式碼需要junit5和mockito的支援,使用時需要引入相關的依賴 -
增加assert校驗邏輯,看是不是想要的結果,目前外掛不會自動生成assertEquals等斷言程式碼。 -
運用引數化測試能力,複製一份生成的json檔案並修改輸入資料,多組測試
3 外掛實現介紹

4 後期規劃
-
mock資料可定製,目前的想法是 -
固定值比如目前的String: test、Integer和boolean: 0、1 -
測試者使用配置模版,比如txt檔案包含keyValue對 -
使用Faker,對於name、email、phone這種特定傾向的資料進行特色自動生成 -
自動分支測試,這一塊的想法目前主要針對if來做,需要一定的時間。 -
其他
六 寫在最後

Elasticsearch實戰進階營
點選閱讀原文檢視詳情
關鍵詞
方法
容器
資料
程式碼
單元測試