天天看點

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

一、背景

很多公司對分支單測覆寫率會有一定的要求,比如 單測覆寫率要達到 60% 或者 80%才可以釋出。

有時候工期相對緊張,就優先開發功能,測試功能,然後再去補單元測試。

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

但是編寫單元測試又比較浪費時間,有沒有能夠很大程度上自動化生成單元測試的插件,自己簡單改改即可呢?

自己嘗試在 Idea 插件庫裡搜尋相關插件并去嘗試使用,發現

TestMe

還可以。後面和其他同學交流,謊伴 同學推薦他一直在用的

Squaretest

,我試用之後發現相當不錯。

在這裡簡單介紹這兩個插件。

如果先嘗試其他單元測試相關插件,可以在 IDEA 裡 或者點這裡:

https://plugins.jetbrains.com/search?orderBy=downloads&tags=Unit%20testing

二、推薦工具

2.1 Squaretest

2.1.1 使用介紹

官網位址:

https://squaretest.com/

官方使用者手冊:

https://squaretest.com/#user_guide

官網插件位址

https://plugins.jetbrains.com/plugin/10405-squaretest

優點:生成的代碼比較規整,生成的代碼比較,幫助構造一些參數等。

缺點:不使用 Confirm Mock功能時,對Spring 的 Bean 生成單測代碼時,如果屬性是通過 @Setter 注解注入,則不會生成 @Mock 屬性 ;如果想實作暫時隻能自己修改模闆來支援(後面會給出)。

使用方法:

可以在頂部菜單 [Squaretest] 菜單中選擇第一個或者使用對應快捷鍵建立單元測試。

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

生成的代碼:

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結
這個例子比較簡單,隻是給大家示範如何使用,實際使用中類複雜時,就能體會到該插件的強大。

示例代碼:

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserManager {

    @Resource
    private UserDAO userDAO;

    public List<UserDO> someThing(Param param) {

        List<UserDO> result = new ArrayList<>();

        if(param == null) {
            return result;
        }

        List<String> userIds = param.getUserIds();
        if(CollectionUtils.isEmpty(userIds)) {
            return result;
        }

        List<UserDO> users = userDAO.findByIds(userIds);
        if(CollectionUtils.isEmpty(users)) {
            return result;
        }

      return  users.stream().filter(UserDO::getCanShow).collect(Collectors.toList());
    }

}           
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class UserManagerTest {

    @Mock
    private UserDAO mockUserDAO;

    @InjectMocks
    private UserManager userManagerUnderTest;

    @Test
    public void testSomeThing() {
        // Setup
        final Param param = new Param();
        param.setUserIds(Arrays.asList("value"));
        param.setOthers("others");

        // Configure UserDAO.findByIds(...).
        final UserDO userDO = new UserDO();
        userDO.setCanShow(false);
        userDO.setName("name");
        final List<UserDO> userDOS = Arrays.asList(userDO);
        when(mockUserDAO.findByIds(Arrays.asList("value"))).thenReturn(userDOS);

        // Run the test
        final List<UserDO> result = userManagerUnderTest.someThing(param);

        // Verify the results
    }

    @Test
    public void testSomeThing_UserDAOReturnsNoItems() {
        // Setup
        final Param param = new Param();
        param.setUserIds(Arrays.asList("value"));
        param.setOthers("others");

        when(mockUserDAO.findByIds(Arrays.asList("value"))).thenReturn(Collections.emptyList());

        // Run the test
        final List<UserDO> result = userManagerUnderTest.someThing(param);

        // Verify the results
        assertEquals(Collections.emptyList(), result);
    }
}
           

官方示範1:選擇性生成測試代碼

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

官方示範2:選擇需要 mock 的屬性

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

官方示例3:在單測裡寫 test 即可選擇需要測試的方法自動生成測試代碼

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

2.2.2 定制化

前面講到預設的模闆,對Spring 的 Bean 生成單測代碼時,如果通過 xml 方式聲明 bean ,屬性都是通過 @Setter 注解注入,則不會生成 @Mock 屬性 。

public class UserManager {

    @Setter
    private UserDAO userDAO;

    public List<UserDO> someThing(Param param) {
// 省略
    }

}
           

可以使用 Confirm Mocks 功能選擇該屬性需要 Mock

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結
告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

該插件也支援對生成的模闆進行調整:

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

還可以對模闆進行簡單修改,所有 @Setter 都會自動加上 @Mock 注解:

1526 行:

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結
告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

在依賴的注解屬性中添加

Setter

注解即可。

## Add the simple names or cannonical names of any custom dependency annotations to the method call below.
        #set($dependencyAnnotatedFields = $sourceClass.fieldsAnnotatedWith('Inject', 'Setter','Autowired', 'Resource', 'PersistenceContext'))           

如果使用 powermock ,需要進行修改 1502 -1506 行:

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結
#set($mockitoRunnerCanonicalName = 'org.powermock.modules.junit4.PowerMockRunner')
#set($mockitoRunnerName = 'PowerMockRunner')
              

删除 部分

#if(!$ClassUtils.isInTestClasspath('org.mockito.junit.MockitoJUnitRunner') && $ClassUtils.isInTestClasspath('org.mockito.runners.MockitoJUnitRunner'))
        #set($mockitoRunnerCanonicalName = 'org.mockito.runners.MockitoJUnitRunner')
    #end           

2.2 TestMe

2.2.1 使用介紹

插件官網位址

https://plugins.jetbrains.com/plugin/9471-testme

功能:

自動生成 Java JUnit 4/5, TestNG 單元測試

自動生成 Mockito mocks

自動生成 測試參數和斷言語句

自動生成相關 mock 方法

IDEA 菜單: Code->TestMe, Code->Generate

優點:Spring 的 Bean 生成單測代碼時,即使 @Component 這類注解标注,屬性通過 Setter 注解注入時,也會自動給添加 @Mock 和 @InjectMock 這類屬性。

缺點:預設模闆會在生成的方法上都加上 throws Exception

示例代碼1:

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

或者直接使用快捷鍵

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結
告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

示例代碼2:

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserManager {

    @Resource
    private UserDAO userDAO;

    public List<UserDO> someThing(Param param) {

        List<UserDO> result = new ArrayList<>();

        if(param == null) {
            return result;
        }

        List<String> userIds = param.getUserIds();
        if(CollectionUtils.isEmpty(userIds)) {
            return result;
        }

        List<UserDO> users = userDAO.findByIds(userIds);
        if(CollectionUtils.isEmpty(users)) {
            return result;
        }

      return  users.stream().filter(UserDO::getCanShow).collect(Collectors.toList());
    }

}
           

生成的代碼

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Arrays;
import java.util.List;

import static org.mockito.Mockito.*;

public class UserManagerTest {
    @Mock
    UserDAO userDAO;
    @InjectMocks
    UserManager userManager;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testSomeThing() throws Exception {
        when(userDAO.findByIds(any())).thenReturn(Arrays.<UserDO>asList(new UserDO()));

        List<UserDO> result = userManager.someThing(new Param());
        Assert.assertEquals(Arrays.<UserDO>asList(new UserDO()), result);
    }
}

//Generated with love by TestMe :) Please report issues and submit feature requests at: http://weirddev.com/forum#!/testme           

自己在此基礎上簡單修改即可。

大家還可以根據自己需要對模闆進行修改:

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

預設模闆存在幾個問題:

1、沒有在類上增加

@RunWith(MockitoJUnitRunner.class)

注解

2、單元測試方法後面預設會帶上 throws Exception 沒有太大必要

3、底部 TestMe Footer.java 的内容不需要

4、@Mock 和 @InjectMock 之間沒空行

對 Junit4 & mockito 複制一份(原始檔案是隻讀的)進行修改

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

生成一個 Copy of Junit4 & mockito 的模闆,可以對其進行修改

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

修改後的模闆:

#parse("Copy of TestMe macros.java")
#set($hasMocks=$MockitoMockBuilder.hasMockable($TESTED_CLASS.fields))
#if($PACKAGE_NAME)
package ${PACKAGE_NAME};
#end

import static org.junit.Assert.*;
import org.junit.Test;
#if($hasMocks)
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.junit.Assert;
//import static org.mockito.Mockito.*;
#end

#parse("File Header.java")
@RunWith(MockitoJUnitRunner.class)
public class ${CLASS_NAME} {
#renderMockedFields($TESTED_CLASS.fields)

#renderTestSubjectInit($TESTED_CLASS,$TestSubjectUtils.hasTestableInstanceMethod($TESTED_CLASS.methods),$hasMocks)
#if($hasMocks)

    @Before
    public void setUp() {
    }
#end
#foreach($method in $TESTED_CLASS.methods)
#if($TestSubjectUtils.shouldBeTested($method))

    @Test
    public void #renderTestMethodName($method.name)(){
#if($MockitoMockBuilder.shouldStub($method,$TESTED_CLASS.fields))
#renderMockStubs($method,$TESTED_CLASS.fields)

#end
        #renderMethodCall($method,$TESTED_CLASS.name)
#if($method.hasReturn())        #renderJUnitAssert($method)#end
    }
#end
#end
}
           

然後我們生成單元測試時選擇該模闆:

告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結

發現生成的代碼格式好了不少:

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.Arrays;
import java.util.List;

import static org.junit.Assert.assertEquals;

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class UserManagerTest {
    @Mock
    UserDAO userDAO;

    @InjectMocks
    UserManager userManager;

    @Before
    public void setUp() {
    }

    @Test
    public void testSomeThing() {
        when(userDAO.findByIds(any())).thenReturn(Arrays.<UserDO>asList(new UserDO()));

        List<UserDO> result = userManager.someThing(new Param());
        assertEquals(Arrays.<UserDO>asList(new UserDO()), result);
    }
}
           

三、單測高效構造參數和傳回值神器

我們還可以借助其他工具,自動生成測試的參數或者傳回值。

https://github.com/j-easy/easy-random

可以參考我之前的一篇文章:

《Java高效構造對象的神器:easy-random 簡介》
一兩行就可以構造一個非常複雜的對象或者對象清單。
《Java 單元測試生成測試字元串的神器:java-faker》
如果我們想要随機構造人名、地名、天氣、學校、顔色、職業,甚至符合某正規表達式的字元串

四、總結

靈活使用單元測試自動生成插件,可以節省很多時間。

大家可以安裝并試用這兩個插件,然後根據自己的喜好,選擇最适合自己的那個插件使用。

也可以根據自己的喜好,對模闆進行調整。

此外,大家不要對插件要求太高,生成的單元測試或多或少還是需要自己進行簡單修改,如修改下參數、增加幾個斷言等。

創作不易,如果本文對你有幫助,歡迎點贊、收藏加關注,你的支援和鼓勵,是我創作的最大動力。
告别加班/解放雙手提高單測覆寫率之Java 自動生成單測代碼神器推薦一、背景二、推薦工具三、單測高效構造參數和傳回值神器四、總結