如果你对软件测试、面试经验交流感兴趣欢迎加入软件测试技术群:695458161,群里发放的免费资料都是笔者十多年测试生涯的精华。还有同行大神一起交流技术哦。

JAVA自动化之Junit单元测试框架详解

上一篇 / 下一篇  2020-04-26 16:20:42 / 个人分类:WEB自动化

一、JUnit概述&配置

1、Junit是什么?

Junit是一个Java 编程语言的开源测试框架,用于编写和运行测试。官网 地址:https://junit.org/junit4/

2、Maven配置

?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>junit</groupId>
    <artifactId>junitTest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

二、Assertions 断言

JUnit提供了一些辅助的函数,用来判断被测试的方法是否如我们预期的效果一样正常执行。这些辅助函数被称之为断言。
常用断言:

方法示例功能
assertArrayEqualsassertArrayEquals("message", expected, actual);判断两个数组是否相等
assertEqualsassertEquals("message", "text", "text");判断两个对象是否相等
assertFalseassertFalse("failure - should be false", false);判断表达式是否为false
testAssertTrueassertTrue("failure - should be true", true);判断表达式是否为true
assertNotNullassertNotNull("should not be null", new Object());判断是否非空
assertNullassertNull("should be null", null);判断是否为空
assertNotSameassertNotSame("should not be same Object", new Object(), new Object());判断是否为不同对象
assertSameassertSame("should be same", aNumber, aNumber);判断是否为同一对象
..................

三、Test Runners 测试运行器

如果你觉得此文对你有帮助,如果你对软件测试、面试经验感兴趣欢迎加入软件测试技术群:695458161,群里发放的免费资料都是笔者十多年测试生涯的精华。还有同行大神一起交流技术哦。
笔者来自公众号:软测之家

JUnit中所有的测试方法都是由测试运行器负责执行。当一个类被@RunWith注释或拓展了一个@RunWith注释的类,JUnit将会使用引用的类来执行测试,而不是使用JUnit内置的运行器。

org.junit.runner.JUnitCore.runClasses(TestClass1.class, ...);

Specialized Runners:
Suite:Suite是一个标准的运行器,允许手动构建包含来自许多类的测试集。
Parameterized:Parameterized是一个实现参数化测试的标准运行器。运行参数化测试类时,测试方法和测试数据进行合并来创建测试实例。
Categories:Categories运行器来制定分类,定义测试被包含或排除。

四、Aggregating tests in suites 套件

测试套件用于捆绑几个单元测试用例并且一起执行他们,使用@RunWith 和 @Suite 注解。

@RunWith(Suite.class)@Suite.SuiteClasses({AssertTests.class, CalculatorTest.class})publicclassSuiteTest{// the class remains empty, used only as a holder for the above annotations}

五、Test execution order 执行顺序

要改变测试执行的顺序只需要在测试类上使用 @FixMethodOrder注解,并指定一个可用的MethodSorter即可:
@FixMethodOrder(MethodSorters.DEFAULT):JUnit默认使用一个确定的,但不可预测的顺序
@FixMethodOrder(MethodSorters.JVM): 保留测试方法的执行顺序为JVM返回的顺序,每次测试的执行顺序有可能会所不同
@FixMethodOrder(MethodSorters.NAME_ASCENDING):根据测试方法的方法名排序,按照词典排序规则(ASC从小到大递增)

@FixMethodOrder(MethodSorters.NAME_ASCENDING)publicclassExecutionOrderTest{@TestpublicvoidtestB(){
        System.out.println("second");
    }@TestpublicvoidtestA(){
        System.out.println("first");
    }@TestpublicvoidtestC(){
        System.out.println("third");
    }
}

运行结果:

first
second
third

六、Expected Exceptions 异常测试

用于测试某一方法是否抛出了正确的异常。
1、@Test(expected=xxx)方式:当抛出的异常与expected参数指定的异常相同时,测试通过。
2、try...fail...catch...方式:捕捉具体待测语句的异常信息并断言,当没有异常被抛出的时候fail方法会被调用,输出测试失败的信息。
3、ExpectedException Rule方式:使用Rule标记来指定一个ExpectedException,并在测试相应操作之前指定期望的Exception类型。

publicclassExpectedExceptionsTest{//方法一:@Test(expected=xxx)@Test(expected = IndexOutOfBoundsException.class)publicvoidempty(){newArrayList<Object>().get(0);
    }//方法二:try...fail...catch...  当没有异常被抛出的时候fail方法会被调用,输出测试失败的信息。@TestpublicvoidtestExceptionMessage(){try{newArrayList<Object>().get(0);
            fail("Expected an IndexOutOfBoundsException to be thrown");
        }catch(IndexOutOfBoundsException anIndexOutOfBoundsException) {
            assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
        }
    }//方法三:在测试之前使用Rule标记来指定一个ExpectedException,并在测试相应操作之前指定期望的Exception类型(如IndexOutOfBoundException.class)@RulepublicExpectedException thrown = ExpectedException.none();@TestpublicvoidshouldTestExceptionMessage()throwsIndexOutOfBoundsException{
        List<Object> list =newArrayList<Object>();
        thrown.expect(IndexOutOfBoundsException.class);
        thrown.expectMessage("Index: 0, Size: 0");
        list.get(0);
    }
}

七、Matchers and assertThat

JUnit4.4引入了Hamcrest框架,Hamcest提供了一套匹配符Matcher,这些匹配符更接近自然语言,可读性高,更加灵活。并且使用全新的断言语法assertThat,结合Hamcrest提供的匹配符,只用这一个方法,就可以实现所有的测试。
assertThat语法:
assertThat(T actual, Matcher matcher);
assertThat(String reason, T actual, Matcher matcher);
其中reason为断言失败时的输出信息,actual为断言的值或对象,matcher为断言的匹配器,里面的逻辑决定了给定的actual对象满不满足断言。
Matchers详见:
http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html

八、Ignoring tests 忽略测试

  • 方法用 @Ignore 注解了将不会被执行
  • 类用 @Ignore 注解后,其下所有测试方法将不会被执行
publicclassIgnoreTest{@Ignore("Test is ignored as a demonstration")@TestpublicvoidtestSame(){
        assertThat(1, is(1));
    }
}

九、Timeout for tests 超时测试

@Timeout 注解用来测试特定方法的执行时间。如果测试方法的执行时间大于指定的超时参数,测试方法将抛出异常,测试结果为失败。指定的超时参数单位为毫秒
1、@Test注解上的timeout参数,作用域为方法,单位毫秒

@Test(timeout =2000)publicvoidtestSleepForTooLong()throwsException{
        log +="ran1";
        TimeUnit.SECONDS.sleep(100);// sleep for 100 seconds}

2、Timeout Rule,作用域为测试类

publicclassTimeoutTests{publicstaticString log;privatefinalCountDownLatch latch =newCountDownLatch(1);@RulepublicTimeout globalTimeout = Timeout.seconds(3);// 3 seconds max per method tested@TestpublicvoidtestSleepForTooLong()throwsException{
        log +="ran1";
        TimeUnit.SECONDS.sleep(100);// sleep for 100 seconds}@TestpublicvoidtestBlockForever()throwsException{
        log +="ran2";
        latch.await();// will block}
}

十、Parameterized tests 参数化测试

参数化测试允许开发人员使用不同的值反复运行同一个测试。创建参数化测试步骤:

  • 用 @RunWith(Parameterized.class) 来注释 test 类。
  • 创建一个由 @Parameters 注释的公共的静态方法,它返回一个对象的集合(数组)来作为测试数据集合。
  • 创建一个公共的构造函数,接受测试数据。
  • 为每一列测试数据创建一个实例变量。
  • 用实例变量作为测试数据的来源来创建测试用例
    1、Constructor方式
@RunWith(Parameterized.class)publicclassFibonacciTest{@Parameters(name ="{index}: fib({0})={1}")publicstaticCollection<Object[]> data() {returnArrays.asList(newObject[][] {
                {0,0}, {1,1}, {2,1}, {3,2}, {4,3}, {5,5}, {6,8}
        });
    }privateintfInput;privateintfExpected;publicFibonacciTest(intinput,intexpected){this.fInput = input;this.fExpected = expected;
    }@Testpublicvoidtest(){
        assertEquals(fExpected, Fibonacci.compute(fInput));
    }
}

2、Field injection方式

@RunWith(Parameterized.class)publicclassFibonacciTest{@Parameters(name ="{index}: fib({0})={1}")publicstaticCollection<Object[]> data() {returnArrays.asList(newObject[][] {
                {0,0}, {1,1}, {2,1}, {3,2}, {4,3}, {5,5}, {6,8}
        });
    }@Parameter// first data value (0) is defaultpublic/* NOT private */intfInput;@Parameter(1)public/* NOT private */intfExpected;@Testpublicvoidtest(){
        assertEquals(fExpected, Fibonacci.compute(fInput));
    }
}

十一、Assumptions with assume 假定测试

使用Assumptions类中的假设方法时,当假设不成立时会报错,但是测试会显示被ignore忽略执行。也就是当一个类中有多个测试方法时,其中一个假设测试方法假设失败,其他的测试方法全部成功,那么该测试类也会显示测试成功。
假设方法适用于:在不影响测试是否成功的结果的情况下根据不同情况执行相关代码。

publicclassAssumptionsTest{@TestpublicvoidtestAssumTrue(){
        System.out.println("test");
        assumeTrue(3>5);//该方法中下面所有的代码在上面假设的条件成立后执行//如果上述假设不成立,则会忽略执行该行下面的代码,并报错System.out.println("assume is true!");
    }@TestpublicvoidtestAssumFalse(){
        assumeFalse(3>5);
        System.out.println("assume is true!");
    }
  }

以下语法JUnit5支持:

@TestpublicvoidtestAssumTrueMessage(){
        assumeTrue(3<5,//第二个参数为当第一个参数不成立时,输出的自定义错误信息() ->"Aborting test: not on developer workstation");
        System.out.println("assume is true!");
    }@TestpublicvoidtestAssumeTrueLambda(){//这个方法的第一个参数为函数式接口,无参数返回值为booleanassumeTrue(()->{
            System.out.println("in assumeTrue");booleanflag =false;returnflag;
        });
        System.out.println("out assumeTrue");
    }@TestpublicvoidtestAssumThat(){
        assumingThat(3>5,
                () -> {//与上述方法不同的是,仅当前面假设成立时,才会执行这里面的语句//且只会影响到该lambda表达式中的代码assertEquals(2,2);
                });//此处的断言不受上述assumingThat限制,在所有情况下都会执行System.out.println("no effect");
        assertEquals("a string","a string");
    }

十二、Rules 规则

一个JUnit Rule就是一个实现了TestRule的类,用来在每个测试方法的执行前后执行一些代码。
1、框架自带的Rule
JUnit自带很多已经实现过好了的JUnit Rule,比如Timeout,ExpectedException等等。
2、自定义Rule
自定义一个Rule就是implement一个TestRule interface,实现一个叫apply()的方法。
例:在测试方法运行之前,记录测试方法所在的类名和方法名,然后在测试方法运行之后打印出来。

publicclassMethodNameExampleimplementsTestRule{@OverridepublicStatementapply(finalStatement base,finalDescription description){returnnewStatement() {@Overridepublicvoidevaluate()throwsThrowable{//base.evaluate()之前为测试方法运行之前所做操作String className = description.getClassName();
                String methodName = description.getMethodName();//运行测试方法base.evaluate();//base.evaluate()之后为测试方法运行之后所做操作System.out.println("Class name: "+className +", method name: "+methodName);
            }
        };
    }
}


publicclassRuleTest2{@RulepublicMethodNameExample methodNameExample =newMethodNameExample();@Testpublicvoidaddition_isCorrect()throwsException{
        assertEquals(4,2+2);
    }@Testpublicvoidmulitiplication_isCorrect()throwsException{
        assertEquals(4,2*2);
    }
}

十三、Theories

在参数化测试中,我们需要给定所有具体的测试数据组。而在Theories测试中,用户只需给定了一些数据,JUnit自动利用这些数据组合出各种各种可能的组合来执行测试。

1、内置实现

(1)@DataPoints注解静态变量方式

@RunWith(Theories.class)publicclassTheoryTest{//允许的最大误差privatestaticfinaldoubleDELTA =0.01;/*@DataPoints注解静态变量*/@DataPointpublicstaticintZERO =0;@DataPointpublicstaticintTWO =2;@DataPointpublicstaticintEIGHT =8;//标志这个测试为Theory测试@TheorypublicvoidtestDivide(intdividend,intdivisor){//跳过除数为0的caseassumeThat(divisor, not(0));//Calculator.divide(dividend, divisor)方法返回他们相除的结果assertEquals(dividend / divisor, Calculator.divide(dividend, divisor), DELTA);
        System.out.println("Passed with: dividend="+ dividend +", divisor="+ divisor);
    }
}

(2)@DataPoints注解静态方法方式

@RunWith(Theories.class)publicclassTheoryTest{//允许的最大误差privatestaticfinaldoubleDELTA =0.01;/*@DataPoints注解一个静态方法*/@DataPointspublicstaticint[] getTestData() {returnnewint[]{0,2,8};
    }//标志这个测试为Theory测试@TheorypublicvoidtestDivide(intdividend,intdivisor){//跳过除数为0的caseassumeThat(divisor, not(0));//Calculator.divide(dividend, divisor)方法返回他们相除的结果assertEquals(dividend / divisor, Calculator.divide(dividend, divisor), DELTA);
        System.out.println("Passed with: dividend="+ dividend +", divisor="+ divisor);
    }
}

@DataPoint用于注解静态变量(或静态方法),表示这个变量是个数据点。当执行testDivide这个Theory测试时,JUnit会把所有的DataPoint数据两两组合,形成一组组的测试数据,并用这些数据分别执行测试。执行上面的测试会输出以下结果:

Passed with: dividend=0, divisor=2
Passed with: dividend=0, divisor=8
Passed with: dividend=2, divisor=2
Passed with: dividend=2, divisor=8
Passed with: dividend=8, divisor=2
Passed with: dividend=8, divisor=8

(3)如果需要限定某个参数,可以使用@TestOn注解

importorg.junit.experimental.theories.Theories;importorg.junit.experimental.theories.Theory;importorg.junit.experimental.theories.suppliers.TestedOn;importorg.junit.runner.RunWith;importstaticorg.junit.Assert.assertEquals;@RunWith(Theories.class)publicclassTheoryTest{//允许的最大误差privatestaticfinaldoubleDELTA =0.01;//如果需要限定某个参数,可以使用@TestOn注解@Theorypublicvoid

TAG:

zjj123的个人空间 引用 删除 zjj123   /   2020-04-28 20:42:52
厉害
 

评分:0

我来说两句

诸葛_

诸葛_

如果你对软件测试、面试经验交流感兴趣欢迎加入软件测试技术群:695458161,群里发放的免费资料都是笔者十多年测试生涯的精华。还有同行大神一起交流技术哦。

日历

« 2024-04-17  
 123456
78910111213
14151617181920
21222324252627
282930    

数据统计

  • 访问量: 22080
  • 日志数: 38
  • 建立时间: 2020-04-01
  • 更新时间: 2020-06-29

RSS订阅