llt

596 阅读8分钟

Junit

使用方法

在需要测试的方法上加@Test注解即可

import static org.junit.jupiter.api.Assertions.assertEquals;​import org.junit.jupiter.api.Test;​class FirstJUnit5Tests {​    @Test    void myFirstTest() {        assertEquals(2, 1 + 1);    }}

注解

注解描述
@Test表示方法是测试方法。与JUnit4的@Test注解不同的是,这个注解没有声明任何属性,因为JUnit Jupiter中的测试扩展是基于他们自己的专用注解来操作的。除非被覆盖,否则这些方法可以继承。
@ParameterizedTest表示方法是参数化测试。 除非被覆盖,否则这些方法可以继承。
@RepeatedTest表示方法是用于重复测试的测试模板。除非被覆盖,否则这些方法可以继承。
@TestFactory表示方法是用于动态测试的测试工厂。除非被覆盖,否则这些方法可以继承。
@TestInstance用于为被注解的测试类配置测试实例生命周期。 这个注解可以继承。
@TestTemplate表示方法是测试用例的模板,设计为被调用多次,调用次数取决于自注册的提供者返回的调用上下文。除非被覆盖,否则这些方法可以继承。
@DisplayName声明测试类或测试方法的自定义显示名称。这个注解不被继承。
@BeforeEach表示被注解的方法应在当前类的每个@Test@RepeatedTest@ParameterizedTest@TestFactory方法之前执行; 类似于JUnit 4的@Before。 除非被覆盖,否则这些方法可以继承。
@AfterEach表示被注解的方法应在当前类的每个@Test@RepeatedTest@ParameterizedTest@TestFactory方法之后执行; 类似于JUnit 4的@After。 除非被覆盖,否则这些方法可以继承。
@BeforeAll表示被注解的方法应该在当前类的所有@Test@RepeatedTest@ParameterizedTest@TestFactory方法之前执行; 类似于JUnit 4的@BeforeClass。 这样的方法可以继承(除非被隐藏或覆盖),并且必须是静态的(除非使用“per-class”测试实例生命周期)。
@AfterAll表示被注解的方法应该在当前类的所有@Test@RepeatedTest@ParameterizedTest@TestFactory方法之后执行; 类似于JUnit 4的@AfterClass。 这样的方法可以继承(除非被隐藏或覆盖),并且必须是静态的(除非使用“per-class”测试实例生命周期)。
@Nested表示被注解的类是一个嵌套的非静态测试类。除非使用“per-class”测试实例生命周期,否则@BeforeAll@AfterAll方法不能直接在@Nested测试类中使用。 这个注解不能继承。
@Tag在类或方法级别声明标签,用于过滤测试; 类似于TestNG中的test group或JUnit 4中的Categories。这个注释可以在类级别上继承,但不能在方法级别上继承。
@Disabled用于禁用测试类或测试方法; 类似于JUnit4的@Ignore。这个注解不能继承。
@ExtendWith用于注册自定义扩展。 这个注解可以继承。

eg:

import static org.junit.jupiter.api.Assertions.fail;import org.junit.jupiter.api.AfterAll;import org.junit.jupiter.api.AfterEach;import org.junit.jupiter.api.BeforeAll;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Disabled;import org.junit.jupiter.api.Test;class StandardTests {    @BeforeAll    static void initAll() {    }    @BeforeEach    void init() {    }    @Test    void succeedingTest() {    }    @Test    void failingTest() {        fail("a failing test");    }    @Test    @Disabled("for demonstration purposes")    void skippedTest() {        // not executed    }    @AfterEach    void tearDown() {    }    @AfterAll    static void tearDownAll() {    }}

断言

package com.yiibai.junit;import static org.junit.Assert.*;import org.junit.Test;public class AssertionsTest {    @Test    public void test() {        String obj1 = "junit";        String obj2 = "junit";        String obj3 = "test";        String obj4 = "test";        String obj5 = null;        int var1 = 1;        int var2 = 2;        int[] arithmetic1 = { 1, 2, 3 };        int[] arithmetic2 = { 1, 2, 3 };        assertEquals(obj1, obj2);        assertSame(obj3, obj4);        assertNotSame(obj2, obj4);        assertNotNull(obj1);        assertNull(obj5);        assertTrue(var1  var2);        assertArrayEquals(arithmetic1, arithmetic2);    }}//原文出自【易百教程】,商业转载请联系作者获得授权,非商业请保留原文链接:https://www.yiibai.com/junit/junit-assertions.html

Jmockit

基本使用

JMockit是一款Java类/接口/对象的Mock工具.

Java Mock工具很多,比如easyMock,Mockito等等。为什么要选择JMockit呢?其实也没有啥特别原因,Mock工具的原理都差不多,就看Mock工具封装的API是否易用了。JMockit的API易用,丰富! 写出来的Mock程序代码完全面向对象.

//一个简单的类,能用不同语言打招呼

public class HelloJMockit {    // 向JMockit打招呼    public String sayHello() {        Locale locale = Locale.getDefault();        if (locale.equals(Locale.CHINA)) {            // 在中国,就说中文            return "你好,JMockit!";        } else {            // 在其它国家,就说英文            return "Hello,JMockit!";        }    }}

//HelloJMockit类的测试类

public class HelloJMockitTest {     /**     * 测试场景:当前是在中国     */    @Test    public void testSayHelloAtChina() {        // 假设当前位置是在中国        new Expectations(Locale.class) {            {                Locale.getDefault();                result = Locale.CHINA;            }        };        // 断言说中文        Assert.assertTrue("你好,JMockit!".equals((new HelloJMockit()).sayHello()));    }    /**     * 测试场景:当前是在美国     */    @Test    public void testSayHelloAtUS() {        // 假设当前位置是在美国        new Expectations(Locale.class) {            {                Locale.getDefault();                result = Locale.US;            }        };        // 断言说英文        Assert.assertTrue("Hello,JMockit!".equals((new HelloJMockit()).sayHello()));    }}

在上面的例子中,为了对依赖(当前的位置)进行Mock,用简单的3行代码即可搞定。把测试代码的依赖抽象成期待(Expectations),并把期待类Expectations作为本测试程序的内部类,可以任意访问本测试程序类的所有成员,为编写Mock程序提供极大便利。API面向对象特性封装良好。

此外,JMockit还提供了注解,支持泛型的Mock API用于对类/对象的属性,方法(支持static,private,final,native),构造函数,初始代码块(含静态初始代码块)灵活Mock。

jmockit常用api

@Mocked

@Mocked修饰一个类时

 //@Mocked注解用途public class MockedClassTest {    // 加上了JMockit的API @Mocked, JMockit会帮我们实例化这个对象,不用担心它为null    @Mocked    Locale locale;    // 当@Mocked作用于class    @Test    public void testMockedClass() {        // 静态方法不起作用了,返回了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);    }}

@Mocked修饰一个接口/抽象类时

//@Mocked注解用途public class MockedInterfaceTest {    // 加上了JMockit的API @Mocked, JMockit会帮我们实例化这个对象,尽管这个对象的类型是一个接口,不用担心它为null    @Mocked    HttpSession session;    // 当@Mocked作用于interface    @Test    public void testMockedInterface() {        // (返回类型为String)也不起作用了,返回了null        Assert.assertTrue(session.getId() == null);        // (返回类型为原始类型)也不起作用了,返回了0        Assert.assertTrue(session.getCreationTime() == 0L);        // (返回类型为原非始类型,非String,返回的对象不为空,这个对象也是JMockit帮你实例化的,同样这个实例化的对象也是一个Mocked对象)        Assert.assertTrue(session.getServletContext() != null);        // Mocked对象返回的Mocked对象,(返回类型为String)的方法也不起作用了,返回了null        Assert.assertTrue(session.getServletContext().getContextPath() == null);    }}

@Mocked功能总结

通过上述例子,可以看出:@Mocked修饰的类/接口,是告诉JMockit,帮我生成一个Mocked对象,这个对象方法(包含静态方法)返回默认值。
即如果返回类型为原始类型(short,int,float,double,long)就返回0,如果返回类型为String就返回null,如果返回类型是其它引用类型,则返回这个引用类型的Mocked对象。

Expectations

Expectations主要有两种使用方式。

通过引用外部类的Mock对象(@Injectabe,@Mocked,@Capturing)来录制

//Expectations对外部类的mock对象进行录制public class ExpectationsTest {    @Mocked    Calendar cal;    @Test    public void testRecordOutside() {        new Expectations() {            {                // 对cal.get方法进行录制,并匹配参数 Calendar.YEAR                cal.get(Calendar.YEAR);                result = 2016;// 年份不再返回当前小时。而是返回2016年                // 对cal.get方法进行录制,并匹配参数 Calendar.HOUR_OF_DAY                cal.get(Calendar.HOUR_OF_DAY);                result = 7;// 小时不再返回当前小时。而是返回早上7点钟            }        };        Assert.assertTrue(cal.get(Calendar.YEAR) == 2016);        Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7);        // 因为没有录制过,所以这里月份返回默认值 0        Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == 0);    }}

在这个例子中,在Expectations匿名内部类的初始代码块中,我们可以对外部类的任意成员变量,方法进行调用。大大便利我们书写录制脚本。

通过构建函数注入类/对象来录制

在上面的例子中,我们通过引用外部类的Mock对象(@Injectabe,@Mocked,@Capturing)来录制,可是无论是@Injectabe,@Mocked,@Capturing哪种Mock对象,都是对类的方法都mock了,可是有时候,我们只希望JMockit只mock类/对象的某一个方法。怎么办? 看下面的例子就明白啦。

//通过Expectations对其构造函数mock对象进行录制public class ExpectationsConstructorTest2 {    // 把类传入Expectations的构造函数    @Test    public void testRecordConstrutctor1() {        Calendar cal = Calendar.getInstance();        // 把待Mock的类传入Expectations的构造函数,可以达到只mock类的部分行为的目的        new Expectations(Calendar.class) {            {                // 只对get方法并且参数为Calendar.HOUR_OF_DAY进行录制                cal.get(Calendar.HOUR_OF_DAY);                result = 7;// 小时永远返回早上7点钟            }        };        Calendar now = Calendar.getInstance();        // 因为下面的调用mock过了,小时永远返回7点钟了        Assert.assertTrue(now.get(Calendar.HOUR_OF_DAY) == 7);        // 因为下面的调用没有mock过,所以方法的行为不受mock影响,        Assert.assertTrue(now.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate());    }    // 把对象传入Expectations的构造函数    @Test    public void testRecordConstrutctor2() {        Calendar cal = Calendar.getInstance();        // 把待Mock的对象传入Expectations的构造函数,可以达到只mock类的部分行为的目的,但只对这个对象影响        new Expectations(cal) {            {                // 只对get方法并且参数为Calendar.HOUR_OF_DAY进行录制                cal.get(Calendar.HOUR_OF_DAY);                result = 7;// 小时永远返回早上7点钟            }        };        // 因为下面的调用mock过了,小时永远返回7点钟了        Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7);        // 因为下面的调用没有mock过,所以方法的行为不受mock影响,        Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate());        // now是另一个对象,上面录制只对cal对象的影响,所以now的方法行为没有任何变化        Calendar now = Calendar.getInstance();        // 不受mock影响        Assert.assertTrue(now.get(Calendar.HOUR_OF_DAY) == (new Date()).getHours());        // 不受mock影响        Assert.assertTrue(now.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate());    }}

MockUp & Mock

MockUp & @Mock提供的Mock方式
MockUp是使用最多的方法,该方式简单直接,可以清楚看到具体mock的类和方法
对于private和static方法,也可以使用MockUp,只需要去掉static关键字或者将private修改为public,再加上@Mock注解即可。

//Mockup & @Mock的Mock方式public class MockUpTest {    @Test    public void testMockUp() {        // 对Java自带类Calendar的get方法进行定制        // 只需要把Calendar类传入MockUp类的构造函数即可        new MockUp<Calendar>(Calendar.class) {            // 想Mock哪个方法,就给哪个方法加上@Mock, 没有@Mock的方法,不受影响            @Mock            public int get(int unit) {                if (unit == Calendar.YEAR) {                    return 2017;                }                if (unit == Calendar.MONDAY) {                    return 12;                }                if (unit == Calendar.DAY_OF_MONTH) {                    return 25;                }                if (unit == Calendar.HOUR_OF_DAY) {                    return 7;                }                return 0;            }        };        // 从此Calendar的get方法,就沿用你定制过的逻辑,而不是它原先的逻辑。        Calendar cal = Calendar.getInstance(Locale.FRANCE);        Assert.assertTrue(cal.get(Calendar.YEAR) == 2017);        Assert.assertTrue(cal.get(Calendar.MONDAY) == 12);        Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == 25);        Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7);        // Calendar的其它方法,不受影响        Assert.assertTrue((cal.getFirstDayOfWeek() == Calendar.MONDAY));    }}