JMockit 如何 mock 异常

  • Post author:
  • Post category:其他


后来发现用 JMockit 来 mock 异常根本没有之前文中描述的那么复杂,其实还是在那个 result 上,给它赋个异常实例就轻而易举的解决了,只需如此

 new Expectations(MyService.class, ExternalService.class) {
     {
         ExternalService.fetchData();
         result =  new NetworkException("No IPAddress ");
     }
 };

原文可不用看下去了。


做过几篇 JMockit 使用 Expectations 来 Mock 方法,私有方法,私有属性的的日志,今天工作上突然有个需求是要 Mock 异常。现在再也不能为了跑个单元测试而去拔下网线了,也不该人为的去制造其他混乱来测试。开始是想能不能用 Expectations 来 Mock 异常,尚未发现相关的属性可以设置,没有类似 result 那样的属性,比如想像中有个 exception/throwable 属性:

 new Expectations(MyService.class, ExternalService.class) {
     {
         ExternalService.fetchData();
         throwable =  new NetworkException("No IPAddress ");
     }
 };

可是没有 throwable 个属性,至少我还不知道如何用 Expectations 来 Mock 异常,此路不通,所以只得另辟溪径。我们可以直接 new MockUp 来创建类,这比创建一个 MockClass 来得轻量级些。

下面是一个完整例子,由四个类构成,今次列出


1. 要抛出的异常类 NetworkException.java

1
2
3
4
5
6
7

package


cc.unmi;

public


class


NetworkException


extends


RuntimeException {




public


NetworkException(String message) {




super


(message);



}

}


2. 被测试类 MyService.java

1
2
3
4
5
6
7

package


cc.unmi;

public


class


MyService {




public


static


String fetchData(String name) {




return


ExternalService.fetchByHttp(name);



}

}

调用 ExternalService.fetchByHttp() 方法,我们的测试用例将要 Mock 住这个方法,并抛出 NetworkException 异常。


3. 外部类,待 Mock 的 ExternalService.java

01
02
03
04
05
06
07
08
09
10
11
12
13

package


cc.unmi;

public


class


ExternalService {




public


static


String fetchByHttp(String name) {




String result =


null


;



try


{




// do something



}


catch


(Exception ex) {




throw


new


NetworkException(ex.getMessage());



}



return


result;



}

}

这是个示例方法,就上面的代码正常执行时肯定不会抛出 NetworkException 异常。我们假定在 //do something 的代码可能会抛出 NetworkException 异常,但我们可以在 Mock 方法中立即呈现出这个异常来。


4. 测试类 MyService.java,并完成异常的 Mock

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

package


cc.unmi;

import


mockit.Mock;

import


mockit.MockUp;

import


org.junit.Rule;

import


org.junit.Test;

import


org.junit.rules.ExpectedException;

public


class


MyServiceTest {




@Rule



public


ExpectedException expectedEx = ExpectedException.none();



@Test



public


void


testFetchData() {




new


MockUp<ExternalService>() {




@Mock



public


String fetchByHttp(String name) {




throw


new


NetworkException(


"No IPAddress"


);



}



};



expectedEx.expect(NetworkException.


class


);



expectedEx.expectMessage(


"No IPAddress"


);



MyService.fetchData(


"Yanbin"


);



}

}

这里用

new MockUp<>(){@Mock...}

的方式来 Mock 方法,我们也可以用

@MockClass..@Mock

的方式来创建 Mock 类/方法,但要笨重些。目前我们项目中基本摒弃了用 @MockClass 创建外部 Mock 类的做法,因为一般 Mock 类一般是与某一个测试方法紧密联系着的。

也要注意到 fetchByHttp() 本是个静态方法,但此处不能写成 public String static fetchByHttp(), 但去掉 static 也不会影响到 Mock 的效果。

JUnit 关于异常的测试可以参考

JUnit 4 如何正确测试异常

测试运行成功,能够断言到所期待的异常类型和消息。

下面为演示 Mock 异常的正确而有效性,故意把

expectedEx.epectMessage(“No IPAddress”)

改成

expectedEx.epectMessage(“NoNo IPAddress”)

,这样 JUnit  更能爆出有助于理解的错误信息来


JMockit_mock_exception

上面的错误信息告诉我们

第一个断言

expectedEx.expect(NetworkException.class)

是成功的

期待错误消息

NoNo IPAddress

,但得到的是

No IPAddress

,这正好从反面说明了我们 Mock 异常是成功的。