jmockit使用總結-MockUp重點介紹

  公司對開發人員的單元測試要求比較高,要求分支覆蓋率、行覆蓋率等要達到60%以上等等。項目中已經集成了jmockit這個功能強大的mock框架,學會使用這個框架勢在必行。從第一次寫一點不會,到徹底能夠應付工做要求,期間踩了好多坑,學到了很多東西。下面簡單總結一下jmockit這個框架的使用,重點介紹MockUp的使用,由於項目中都採用此種方式模擬方法。json

1、框架集成app

添加maven依賴框架

 <dependencies>
        <!-- jmockit必須寫在junit以前 -->
        <dependency>
            <groupId>org.jmockit</groupId>
            <artifactId>jmockit</artifactId>
            <version>1.16</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>

 

2、@Mocked模擬方式介紹maven

@Mocked模擬,由錄製、回放、驗證三步驟完成,是對某個類的全部實例的全部方法進行完整的模擬方式。ide

/**
 * 被測試類
 */
public class App {

    public String say() {
        return "Hello World";
    }

    public String say2(){
        return "Hello World 2";
    }

    public static String staticSay() {
        return "Still hello world";
    }
}
/**
 * 測試類
 */
public class AppTest {

    /**
     * 針對類及全部實例的的總體模擬,未寫錄製的方法默認返回0,null等
     */
    @Mocked
    App app;

    @Test
    public void testSay() {

        //錄製,定義被模擬的方法的返回值,能夠錄製多個行爲,寫在一個大括號裏也能夠,多個大括號隔開也能夠
        new Expectations() {{
            app.say();
            result = "say";
        }};

        //回放,調用模擬的方法
        System.out.println(app.say()); //say
        System.out.println(new App().say()); //say
        System.out.println(App.staticSay()); //null

        //驗證
        new Verifications() {{
            //驗證say模擬方法被調用,且調用了2次
            app.say();
            times = 2;

            //驗證staticSay模擬方法被調用,且調用了1次
            App.staticSay();
            times = 1;
        }};

    }
}

 

3、@Injectable模擬方式介紹函數

@Injectable和@Mocked的方式很像,區別是@Injectable僅僅對當前實例進行模擬。 單元測試

/**
 * 測試類
 */
public class AppTest001 {

    /**
     * 僅針對當前實例的總體模擬
     */
    @Injectable
    App app;

    @Test
    public void testSay() {

        //錄製
        new Expectations() {{
            app.say();
            result = "say";
        }};

        //回放
        System.out.println(app.say()); //say,模擬值
        System.out.println(app.say2()); //null,模擬默認值

        final App appNew = new App();
        System.out.println(appNew.say()); //Hello World,未被模擬,方法實際值
        System.out.println(App.staticSay()); //Still hello world,未被模擬,方法實際值

        //驗證
        new Verifications() {
            {
                //驗證say模擬方法被調用
                app.say();
                times = 1;
            }
            {
                appNew.say();
                times = 1;
            }
            {
                //驗證staticSay模擬方法被調用,且調用了1次
                App.staticSay();
                times = 1;
            }
        };

    }
}

 

4、Expectations傳參,局部模擬測試

/**
 * 測試類
 */
public class AppTest002 {

    @Test
    public void testSay() {

        final App app = new App();

        //錄製,帶參數表示局部模擬【針對全部實例的局部模擬,具體到某個方法的模擬,其它方法不模擬】
        new Expectations(App.class) {{
            app.say();
            result = "say";
        }};

        //回放
        System.out.println(app.say()); //say,模擬值
        System.out.println(app.say2()); //Hello World 2 ,未被模擬,方法實際值
        System.out.println(new App().say()); //say,模擬值
        System.out.println(App.staticSay()); //Still hello world,未被模擬,方法實際值

    }
}

 

5、MockUp局部模擬,可重寫原有方法的邏輯,比較靈活,推薦使用spa

/**
 * 測試類
 */
public class AppTest003 {

    @Test
    public void testSay() {
        //局部模擬【針對全部實例的局部模擬,具體到某個方法的模擬,其它方法不模擬】
       new MockUp<App>(App.class){

           @Mock
           String say(){
               return "say";
           }
       };

        //回放
        System.out.println(new App().say()); //say,模擬值
        System.out.println(new App().say2()); //Hello World 2,未被模擬,方法實際值
        System.out.println(App.staticSay()); //Still hello world,未被模擬,方法實際值

    }
}

 

6、MockUp如何模擬私有方法、靜態方法、靜態塊、構造函數等code

1.模擬私有屬性(實例屬性和類屬性),MockUp不支持,採用以下方式

//模擬實例的字段
Deencapsulation.setField(Object objectWithField, String fieldName, Object fieldValue)

//模擬類的靜態字段
Deencapsulation.setField(Class<?> classWithStaticField, String fieldName, Object fieldValue)

2.模擬私有方法,MockUp不支持,採用以下方式

//模擬實例方法,注意參數不能爲null,若是要傳null請使用帶參數類型的另外一個重載方法
Deencapsulation.invoke(Object objectWithMethod, String methodName, Object... nonNullArgs)

//模擬類方法
Deencapsulation.invoke(Class<?> classWithStaticMethod, String methodName, Object... nonNullArgs)

3.模擬靜態方法

和模擬實例方法同樣,去掉static便可

4.模擬靜態塊

 //mock靜態代碼塊
 @Mock
 void $clinit(Invocation invocation){

 }

5.模擬實例塊和構造函數

//mock代碼塊和構造函數
@Mock
void $init(Invocation invocation) {

}

6.模擬接口,MockUp不支持,採用@Capturing

/**
 * 被模擬的接口
 */
public interface IUserService {

    String getUserName( );
}
public class UserServiceImpl implements IUserService {

    @Override
    public String getUserName() {
        return "Bob";
    }
}
/**
 * 接口模擬測試
 */
public class IUserServiceTest {

    /**
     * MockUp不能mock接口方法,能夠用來生成接口實例
     */
    @Test
    public void getUserNameTest001(){
        MockUp<IUserService> mockUp = new MockUp<IUserService>(){

            @Mock
            String getUserName( ){
                return "Jack";
            }
        };

        IUserService obj = new UserServiceImpl();
        System.out.println(obj.getUserName()); //Bob,mock失敗

        obj = mockUp.getMockInstance();
        System.out.println(obj.getUserName()); //Jack,mockUp生成的實例,和本身寫一個接口實現同樣

        obj = new UserServiceImpl();
        System.out.println(obj.getUserName()); //Bob,mock失敗
    }

    /**
     * @Capturing 註解能夠實現mock接口,全部實現類的實例均被mock
     * @param base
     */
    @Test
    public void getUserNameTest002(@Capturing final IUserService base){
        IUserService obj = new UserServiceImpl();
        System.out.println(obj.getUserName()); //mock成功,返回模擬默認值null

        //錄製
        new Expectations(){
            {
                base.getUserName();
                result  = "Jack";
            }
        };
        System.out.println(obj.getUserName()); //Jack

        obj = new IUserService() {
            @Override
            public String getUserName() {
                return "Alice";
            }
        };
        System.out.println(obj.getUserName()); //Jack
    }
    
}

 

7、MockUp模擬方法中調用原方法

/**
 * 模擬方法調用原方法邏輯測試
 */
public class JSONObjectTest {

    @Test
    public void getTest(){
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("a","A");
        jsonObject.put("b","B");
        jsonObject.put("c","C");
        System.out.println(jsonObject.get("a")); //A
        System.out.println(jsonObject.get("b")); //B
        System.out.println(jsonObject.get("c")); //C
        new MockUp<JSONObject>(){
            @Mock
            Object get(Invocation invocation,Object key){
                if("a".equals(key)){
                    return "aa";
                }else{
                    //調用原邏輯
                    return  invocation.proceed(key);
                }
            }
        };
        System.out.println(jsonObject.get("a")); //aa
        System.out.println(jsonObject.get("b")); //B
        System.out.println(jsonObject.get("c")); //C
    }
}

 

8、MockUp單元測試用例單個跑正常,批量跑失敗可能的緣由

1.測試方法使用了共享變量,相互影響。

2.在一個測試方法裏屢次MockUp同一個類,將某個類須要mock的方法均寫在一個new MockUp裏便可解決,緣由未知。

相關文章
相關標籤/搜索