jmockit,mockito和powermock

1,jmockit

在Maven pom.xml配置

1

2

3

4

5

6

  <dependency>

    <groupId>org.jmockit</groupId>

    <artifactId>jmockit</artifactId>

    <version>1.36</version>

    <scope>test</scope>

  </dependency>

JUnit4.x及以下用户特别注意事项

如果你是通过mvn test来运行你的测试程序 , 请确保JMockit的依赖定义出现在JUnit的依赖之前。  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<!-- 先声明jmockit的依赖 -->

   <dependency>

    <groupId>org.jmockit</groupId>

    <artifactId>jmockit</artifactId>

    <version>1.36</version>

    <scope>test</scope>

  </dependency>

<!-- 再声明junit的依赖 -->

   <dependency>

    <groupId>junit</groupId>

    <artifactId>junit</artifactId>

    <version>4.9</version>

    <scope>test</scope>

  </dependency>

Jmockit 有两种mock的方式:

1. Behavior-oriented(Expectations & Verifications)

2. State-oriented(MockUp)

Behavior-oriented是基于行为的mock,对mock目标代码的行为进行模仿,更像黑盒测试。State-oriented 是基于状态的mock,是站在目标测试代码内部的。可以对传入的参数进行检查、匹配,才返回某些结果,类似白盒。而State-oriented的 new MockUp基本上可以mock任何代码或逻辑。非常强大。

Jmockit可以mock的种类包含了:

1. class(abstract, final, static)

2. interface

3. enum

4. annotation

5. native

jmockit基本有三个步骤:

(1)打桩。指定要打桩类和函数,模拟返回结果。这里是new Mockup(NegativeTransDos)。

(2)调用被测方法。被测逻辑执行过程中,之前的打桩数据生效。

(3)判断测试结果是否符合预期。

通过以下方法来模拟类和方法。

new Mockup(类) {

模拟方法{

控制该方法返回结果

    }

}

JMockit的程序结构  

包含了测试属性或测试参数,测试方法,测试方法体中又包含录制代码块,重放测试逻辑,验证代码块。 

//JMockit的程序结构
public class ProgramConstructureTest {
 
    // 这是一个测试属性
    @Mocked
    HelloJMockit helloJMockit;
 
    @Test
    public void test1() {
        // 录制(Record)
        new Expectations() {
            {
                helloJMockit.sayHello();
                // 期待上述调用的返回是"hello,david",而不是返回"hello,JMockit"
                result = "hello,david";
            }
        };
        // 重放(Replay)
        String msg = helloJMockit.sayHello();
        Assert.assertTrue(msg.equals("hello,david"));
        // 验证(Verification)
        new Verifications() {
            {
                helloJMockit.sayHello();
 
                times = 1;
            }
        };
    }
 
    @Test
    public void test2(@Mocked HelloJMockit helloJMockit /* 这是一个测试参数 */) {
        // 录制(Record)
        new Expectations() {
            {
                helloJMockit.sayHello();
                // 期待上述调用的返回是"hello,david",而不是返回"hello,JMockit"
                result = "hello,david";
            }
        };
        // 重放(Replay)
        String msg = helloJMockit.sayHello();
        Assert.assertTrue(msg.equals("hello,david"));
        // 验证(Verification)
        new Verifications() {
            {
                helloJMockit.sayHello();
                // 验证helloJMockit.sayHello()这个方法调用了1次
                times = 1;
            }
        };
    }
}

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

//@Mocked与@Injectable的不同

public class MockedAndInjectable {

    @Test

    public void testMocked(@Mocked Locale locale) {

        // 静态方法不起作用了,返回了null

        Assert.assertTrue(Locale.getDefault() == null);

        // 非静态方法(返回类型为String)也不起作用了,返回了null

        Assert.assertTrue(locale.getCountry() == null);

        // 自已new一个,也同样如此,方法都被mock了

        Locale chinaLocale = new Locale("zh""CN");

        Assert.assertTrue(chinaLocale.getCountry() == null);

    }

    @Test

    public void testInjectable(@Injectable Locale locale) {

        // 静态方法不mock

        Assert.assertTrue(Locale.getDefault() != null);

        // 非静态方法(返回类型为String)也不起作用了,返回了null,但仅仅限于locale这个对象

        Assert.assertTrue(locale.getCountry() == null);

        // 自已new一个,并不受影响

        Locale chinaLocale = new Locale("zh""CN");

        Assert.assertTrue(chinaLocale.getCountry().equals("CN"));

    }

}


@Injectable 也是告诉 JMockit生成一个Mocked对象,但@Injectable只是针对其修饰的实例,而@Mocked是针对其修饰类的所有实例。
此外,@Injectable对类的静态方法,构造函数没有影响。因为它只影响某一个实例嘛!
 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// 邮件服务类,用于发邮件

public interface MailService {

    /**

     * 发送邮件

     

     * @param userId

     *            邮件接受人id

     * @param content

     *            邮件内容

     * @return 发送成功了,就返回true,否则返回false

     */

    public boolean sendMail(long userId, String content);

}

1

2

3

4

5

6

7

8

9

10

11

12

// 用户身份校验  

public interface UserCheckService {

    /**

     * 校验某个用户是否是合法用户

     

     * @param userId

     *            用户ID

     * @return 合法的就返回true,否则返回false 

     */

    public boolean check(long userId);

}//订单服务类 ,用于下订单

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

public class OrderService {

    // 邮件服务类,用于向某用户发邮件。

    MailService mailService;

    // 用户身份校验类,用于校验某个用户是不是合法用户

    @Resource

    UserCheckService userCheckService;

    // 构造函数

    public OrderService(MailService mailService) {

        this.mailService = mailService;

    }

    /**

     * 下订单

     

     * @param buyerId

     *            买家ID

     * @param itemId

     *            商品id

     * @return 返回 下订单是否成功

     */

    public boolean submitOrder(long buyerId, long itemId) {

        // 先校验用户身份

        if (!userCheckService.check(buyerId)) {

            // 用户身份不合法

            return false;

        }

        // 下单逻辑代码,

        // 省略...

        // 下单完成,给买家发邮件

        if (!this.mailService.sendMail(buyerId, "下单成功")) {

            // 邮件发送成功

            return false;

        }

        return true;

    }

}

假设现在我们需要测试OrderService类的submitOrder方法,可是OrderService依赖MailService,UserCheckService类,
在测试过程中,我们并不想真正连结邮件服务器,也不想连结校验用户身份的服务器校验用户身份,怎么办呢?
此时@Tested与@Injectable就排上用场了!请看下面的测试程序:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

    //@Tested与@Injectable搭配使用

public class TestedAndInjectable {

   //@Tested修饰的类,表示是我们要测试对象,在这里表示,我想测试订单服务类。JMockit也会帮我们实例化这个测试对象

   @Tested

   OrderService orderService;

   //测试用户ID

   long testUserId = 123456l;

   //测试商品id

   long testItemId = 456789l;

   // 测试注入方式

   @Test

   public void testSubmitOrder(@Injectable MailService mailService, 

     @Injectable UserCheckService userCheckService) {

    new Expectations() {

       {

         // 当向testUserId发邮件时,假设都发成功了

         mailService.sendMail(testUserId, anyString);

         result = true;

        // 当检验testUserId的身份时,假设该用户都是合法的

         userCheckService.check(testUserId);

        result = true;

         }

         };

    // JMockit帮我们实例化了mailService了,并通过OrderService的构造函数,注入到orderService对象中。 

    //JMockit帮我们实例化了userCheckService了,并通过OrderService的属性,注入到orderService对象中。 

    Assert.assertTrue(orderService.submitOrder(testUserId, testItemId));

    }

}

  1. @Injectable 与 @Mocked的不同

  2. @Tested & @Injectable 两个好基友,通常搭配使用

    为便于演示,我们以电商网站下订单的场景为例:在买家下订单时,电商网站后台程序需要校验买家的身份(是否合法,例如是否在黑名单中),
    若下订单没有问题还要发邮件给买家。    相信下面的代码,你一定能看明白 。

  3. @Tested & @Injectable功能总结

    @Injectable 也表示一个Mocked对象,相比@Mocked,只不过只影响类的一个实例。而@Mocked默认是影响类的所有实例。
    @Tested表示被测试对象。如果该对象没有赋值,JMockit会去实例化它,若@Tested的构造函数有参数,
    则JMockit通过在测试属性&测试参数中查找@Injectable修饰的Mocked对象注入@Tested对象的构造函数来实例化,
    不然,则用无参构造函数来实例化。除了构造函数的注入,JMockit还会通过属性查找的方式,把@Injectable对象注入到@Tested对象中。
     注入的匹配规则:先类型,再名称(构造函数参数名,类的属性名)。若找到多个可以注入的@Injectable,则选择最优先定义的@Injectable对象。
    当然,我们的测试程序要尽量避免这种情况出现。因为给哪个测试属性/测试参数加@Injectable,是人为控制的。
     

  4. 什么测试场景,我们要使用@Tested & @Injectable 

     显然,当我们需要手工管理被测试类的依赖时,就需要用到@Tested & @Injectable。
     两者搭配起来用,JMockit就能帮我们轻松搞定被测试类及其依赖注入细节。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洋气月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值