2、JUnit测试框架

原文链接

文章目录

    • 1、JUnit jar引用
    • 2、编写单元测试步骤
    • 3、常用API
      • 3-1、Assert
      • 3-2、注解
        • @Test @BeforeClass @AfterClass @Before @After
        • @Rule
          • 内置Rule:Timeout
          • 内置Rule:TemporaryFolder
          • 自定义Rule
          • 自定义RuleClass
        • @Parameterized

1、JUnit jar引用

  • 新建的默认Android模板代码会默认在build文件中添加JUnit的依赖,用于测试环境框架一律是testImplementation
  • 而单元测试代码是放在src/test/java下面的
dependencies {
    testImplementation 'junit:junit:4.13.2'
}

2、编写单元测试步骤

  • 新建java类
public class Calculator {

    public int add(int num1, int num2) {
        return num1 + num2;
    }

    public int multiply(int num1, int num2) {
        return num1 * num2;
    }
}
  • 生成单元测试类

2、JUnit测试框架_第1张图片2、JUnit测试框架_第2张图片

  • 修改生成的代码如下
  • 默认生成的测试类继承了TestCase类(这里去除下),在JUnit4中,如果测试类继承了TestCase类,那么所有的Annotation都不会起作用,@Before的方法不会执行,导致@Test 方法报空指针
package com.arthur.unittest;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CalculatorTest {
    Calculator calculator;

    @Before
    public void before() {
        System.out.println("before");
        calculator = new Calculator();
    }

    @After
    public void after() {
        System.out.println("after");
    }

    @Test
    public void testAdd() {
        System.out.println("testAdd: " + this.toString());
        System.out.println("testAdd: " + calculator.toString());
        int sum = calculator.add(1, 2);
        Assert.assertEquals(sum, 3);
    }

    @Test
    public void testMultiply() {
        System.out.println("testMultiply: " + this.toString());
        System.out.println("testMultiply: " + calculator.toString());
        int sum = calculator.multiply1(4, 5);
        Assert.assertEquals(sum, 20);
    }
}
  • 运行之后,如下如下
    • @Test:表示此方法为测试方法
    • @Before:在每个测试方法前执行,可做初始化操作
    • @After:在每个测试方法后执行,可做释放资源操作
before
testAdd: com.arthur.unittest.CalculatorTest@7a6d7e92 两次不是一个示例对象
testAdd: com.arthur.unittest.Calculator@aba625       两次不是一个示例对象
after
before
testMultiply: com.arthur.unittest.CalculatorTest@1b1cfb87 两次不是一个示例对象
testMultiply: com.arthur.unittest.Calculator@821330f      两次不是一个示例对象
after

3、常用API

3-1、Assert

方法名 方法描述
assertEquals 断言传入的预期值与实际值是相等的
assertNotEquals 断言传入的预期值与实际值是不相等的
assertArrayEquals 断言传入的预期数组与实际数组是相等的
assertNull 断言传入的对象是为空
assertNotNull 断言传入的对象是不为空
assertTrue 断言条件为真
assertFalse 断言条件为假
assertSame 断言两个对象引用同一个对象,相当于“==”
assertNotSame 断言两个对象引用不同的对象,相当于“!=”
assertThat 断言实际值是否满足指定的条件
  • 注意:上面的每一个方法,都有对应的重载方法,可以在前面加一个String类型的参数,表示如果断言失败时的提示。
        Assert.assertEquals(20, 20);
        Assert.assertEquals("断言相等的失败提示",20, 20);
        Assert.assertNotNull(obj);
        Assert.assertNotNull("断言不为空的失败提示",obj);
  • assertThat的使用
    • org.junit.Assert.assertThat 方法已经废弃
    • 推荐使用org.hamcrest.MatcherAssert.assertThat
        // Assert.assertThat方法已经废弃,推荐使用MatcherAssert.assertThat
        Assert.assertThat(a,is(b));
        Assert.assertThat(5,not(6));
        MatcherAssert.assertThat(5,equalTo(5));
匹配器 说明 例子
is 断言参数等于后面给出的匹配表达式 assertThat(5, is (5));
not 断言参数不等于后面给出的匹配表达式 assertThat(5, not(6));
equalTo 断言参数相等 assertThat(30, equalTo(30));
containsString 断言字符串包含某字符串 assertThat(“abc”, containsString(“bc”));
startsWith 断言字符串以某字符串开始 assertThat(“abc”, startsWith(“a”));
endsWith 断言字符串以某字符串结束 assertThat(“abc”, endsWith(“c”));
nullValue 断言参数的值为null assertThat(null, nullValue());
greaterThan 断言参数大于 assertThat(4, greaterThan(3));
lessThan 断言参数小于 assertThat(4, lessThan(6));
greaterThanOrEqualTo 断言参数大于等于 assertThat(4, greaterThanOrEqualTo(3));
lessThanOrEqualTo 断言参数小于等于 assertThat(4, lessThanOrEqualTo(6));
closeTo 断言浮点型数在某一范围内 assertThat(4.0, closeTo(2.6, 4.3));
allOf 断言符合所有条件,相当于&& assertThat(4,allOf(greaterThan(3), lessThan(6)));
anyOf 断言符合某一条件,相当于或 assertThat(4,anyOf(greaterThan(9), lessThan(6)));
hasKey 断言Map集合含有此键 assertThat(map, hasKey(“key”));
hasValue 断言Map集合含有此值 assertThat(map, hasValue(value));
hasItem 断言迭代对象含有此元素 assertThat(list, hasItem(element));

3-2、注解

  • 常用注解
    | 注解名 | 含义 |
    | — | — |
    | @Test | 表示此方法为测试方法 |
    | @Before | 在每个测试方法前执行,可做初始化操作 |
    | @After | 在每个测试方法后执行,可做释放资源操作 |
    | @Ignore | 忽略的测试方法 |
    | @BeforeClass | 在类中所有方法前运行。此注解修饰的方法必须是static void |
    | @AfterClass | 在类中最后运行。此注解修饰的方法必须是static void |
    | @RunWith | 指定该测试类使用某个运行器 |
    | @Parameters | 指定测试类的测试数据集合 |
    | @Rule | 重新制定测试类中方法的行为 |
    | @FixMethodOrder | 指定测试类中方法的执行顺序,推荐使用MethodSorters.NAME_ASCENDING |

@Test @BeforeClass @AfterClass @Before @After

package com.arthur.unittest;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class CalculatorTest {
    Calculator calculator;

    @Before
    public void before() {
        System.out.println("before");
        calculator = new Calculator();
    }

    @After
    public void after() {
        System.out.println("after");
    }

    @BeforeClass
    public static void beforeClass() {
        System.out.println("beforeClass");
    }

    @AfterClass
    public static void afterClass() {
        System.out.println("afterClass");
    }

    @Test
    public void testAdd() {
        System.out.println("testAdd: " + calculator.toString());
        int sum = calculator.add(1, 2);
        Assert.assertEquals(sum, 3);
    }

    @Test
    public void testMultiply() {
        System.out.println("testMultiply: " + calculator.toString());
        int sum = calculator.multiply(4, 5);
        Assert.assertEquals(sum, 20);
    }
}
  • 最终执行结果,执行顺序:@BeforeClass –> @Before –> @Test –> @After –> @AfterClass
beforeClass
before
testAdd: com.arthur.unittest.Calculator@7a6d7e92
after
before
testMultiply: com.arthur.unittest.Calculator@34bde49d
after
afterClass

@Rule

内置Rule:Timeout
  • Timeout用于设置每个测试方法的运行时间都不能超过多少毫秒,不然就会标志为失败。而它的实现方式就是在每个方法测试之前都会记录一下时间戳,然后开始倒计时,1秒钟之后如果方法还没有运行结束,就把结果标记为失败。
  • 这里需要注意的一点是Rule需要是public
  • 等效于@Test(timeout = 100)
package com.ecarx.aivoice.test1;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;


public class CalculatorTest {

    /**
     * 测试类中的所有测试方法设置相同的超时时间,超过1000ms就表示用例执行失败
     */
    @Rule
    public Timeout timeout = new Timeout(1000);

    /**
     * 测试失败
     */
    @Test
    public void test1() throws Exception {
        Thread.sleep(1001);
    }

    /**
     * 测试成功
     */
    @Test
    public void test2() throws Exception {
        Thread.sleep(999);
    }
}
内置Rule:TemporaryFolder
  • TemporaryFolder:支持创建文件与目录并且在测试运行结束后将其删除,这对于使用文件系统且独立运行的测试来说很有用。
public class CalculatorTest {
    /**
     * 使用系统临时目录,可在构造方法上加入路径参数来指定临时目录
     */
    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();

    @Test
    public void testTempFolderRule() throws IOException {
        // 在系统的临时目录下创建文件或者目录,当测试方法执行完毕自动删除  
        tempFolder.newFile("test.txt");
        tempFolder.newFolder("test");
    }
}
自定义Rule
  • @Before/@After注解的方法只能作用于当前测试类及其子类,而实现了TestRule的类可以被用于多个测试类,因此JUnit Rule可以降低代码重复度并且更灵活。
  • JUnit Rule可以实现@Before, @BeforeClass, @After, @AfterClass的所有功能,并且会更强大
  • 多个不同的rule对象用于同一个测试用例时,测试人员可以使用RuleChain来设定这些rule的执行先后顺序。
  • 验证代码如下
package com.ecarx.aivoice.test1;

import androidx.annotation.NonNull;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;


public class CalculatorTest {

    @Rule
    public TestRule tempFolder = new TestRule() {

        @NonNull
        @Override
        public Statement apply(@NonNull Statement base, @NonNull Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    // evaluate前执行方法相当于@Before
                    String methodName = description.getMethodName();
                    System.out.println(methodName + "测试开始!" + methodName);

                    // 运行的测试方法
                    base.evaluate();

                    // evaluate后执行方法相当于@After
                    System.out.println(methodName + "测试结束!");
                }
            };
        }
    };

    Calculator calculator = new Calculator();

    @Test
    public void testAdd() {
        System.out.println("testAdd: " + calculator.toString());
        int sum = calculator.add(1, 2);
        Assert.assertEquals(sum, 3);
    }

    @Test
    public void testMultiply() {
        System.out.println("testMultiply: " + calculator.toString());
        int sum = calculator.multiply(4, 5);
        Assert.assertEquals(sum, 20);
    }
}
  • 最终运行结果
testAdd测试开始!testAdd
testAdd: com.ecarx.aivoice.test1.Calculator@5a5a729f
testAdd测试结束!
testMultiply测试开始!testMultiply
testMultiply: com.ecarx.aivoice.test1.Calculator@3932c79a
testMultiply测试结束!
自定义RuleClass
  • @Rule注解是方法级别的,每个测试方法执行时都会执行被@Rule注解的成员变量的方法(类似于@Before)。
  • @ClassRule注解是类级别的,测试类执行时仅会执行一次被@ClassRule注解的静态变量的方法(类似于@BeforeClass)。
package com.ecarx.aivoice.test1;

import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;


public class CalculatorTest {

    @ClassRule
    public static ExternalResource getResource() {
        return new ExternalResource() {
            @Override
            protected void before() throws Throwable {
                System.out.println("before: ");
            }

            @Override
            protected void after() {
                System.out.println("after: ");
            }
        };
    }

    Calculator calculator = new Calculator();

    @Test
    public void testAdd() {
        System.out.println("testAdd: " + calculator.toString());
        int sum = calculator.add(1, 2);
        Assert.assertEquals(sum, 3);
    }

    @Test
    public void testMultiply() {
        System.out.println("testMultiply: " + calculator.toString());
        int sum = calculator.multiply(4, 5);
        Assert.assertEquals(sum, 20);
    }
}
  • 运行结果
before: 
testAdd: com.ecarx.aivoice.test1.Calculator@6b09fb41
testMultiply: com.ecarx.aivoice.test1.Calculator@23f5b5dc
after: 

@Parameterized

  • 在做单元测试时,可能同一个方法需要传很多个不同的参数进行测试,但是写一个测试参数写一个测试方法会比较冗余,这里就可以使用 Parameterized,在测试运行期允许使用者使用不同参数多次运行同一个测试。
  • 待测试类,判断一个数字是否是偶数
public class Number {

    public static int add(int a, int b) {
        return a+b;
    }
}
  • 写法1:构造方法(构造方法的形参列表要和数据集的一行对应)
package com.ecarx.aivoice.test1;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.Collection;
/**
 * 第一步 使用@RunWith(Parameterized.class)注释测试类
 */
@RunWith(Parameterized.class)
public class DemoTest {

    private int number1;
    private int number2;
    private int expectedSum;
    private Number number;

    @Before
    public void initialize() {
        number = new Number();
    }

    /**
     * 第三步 创建一个公共构造函数,它接受相当于一行“测试数据”的内容
     */
    public DemoTest(int number1, int number2, int expectedSum) {
        System.out.println(number1 + ", " + number2 +", "+expectedSum);
        this.number1 = number1;
        this.number2 = number2;
        this.expectedSum = expectedSum;
    }

    /**
     * 第二步 创建一个使用@Parameters注释的公共静态方法,该方法返回一个对象集合作为测试数据集
     *
     * @return
     */
    @Parameterized.Parameters
    public static Collection primeNumbers() {
        return Arrays.asList(new Object[][]{
                {1, 2, 3},// 这是第一行测试数据,1+2=3 1 2前两个是入参,3期望的结果
                {2, 6, 8},// 2+6=8
                {3, 19, 22},// 3+19=22
                {4, 22, 26},
                {5, 23, 28}});
    }

    /**
     * 第四步 测试
     */
    @Test
    public void test() {
        assertEquals(expectedSum, number.add(number1,number2));
    }
}
  • 写法2:注解方式
package com.ecarx.aivoice.test1;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.Collection;

@RunWith(Parameterized.class)
public class DemoTest {
    
    private Number number;

    @Before
    public void initialize() {
        number = new Number();
    }
    
    /**
     * 取这行数据的第0个
     */
    @Parameterized.Parameter(value = 0)
    public int fInputA;
    /**
     * 取这行数据的第1个
     */
    @Parameterized.Parameter(value = 1)
    public int fInputB;

    /**
     * 取这行数据的第2个
     */
    @Parameterized.Parameter(value = 2)
    public int fExpected;

    /**
     * 第二步 创建一个使用@Parameters注释的公共静态方法,该方法返回一个对象集合作为测试数据集
     *
     * @return
     */
    @Parameterized.Parameters
    public static Collection primeNumbers() {
        return Arrays.asList(new Object[][]{
                {1, 2, 3},
                {2, 6, 8},
                {3, 19, 22},
                {4, 22, 26},
                {5, 23, 28}});
    }

    @Test
    public void test() {
        System.out.println("Parameterized Number is : " + fInputA + ", " + fInputB);
        assertEquals(fExpected, number.add(fInputA, fInputB));
    }
}

你可能感兴趣的:(Android,知识库,junit)