原文链接
文章目录
-
- 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、编写单元测试步骤
public class Calculator {
public int add(int num1, int num2) {
return num1 + num2;
}
public int multiply(int num1, int num2) {
return num1 * num2;
}
}
- 修改生成的代码如下
- 默认生成的测试类继承了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(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 {
@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 {
String methodName = description.getMethodName();
System.out.println(methodName + "测试开始!" + methodName);
base.evaluate();
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)
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;
}
@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() {
assertEquals(expectedSum, number.add(number1,number2));
}
}
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();
}
@Parameterized.Parameter(value = 0)
public int fInputA;
@Parameterized.Parameter(value = 1)
public int fInputB;
@Parameterized.Parameter(value = 2)
public int fExpected;
@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));
}
}