单元测试框架 TestNG 和 Junit4的区别

在自动化测试时,经常会用到TestNg 或 Junit 测试框架。那么它们之间有什么联系和区别,如何使用呢?在面试过程中也会经常被问到,结合网上一的些文章,做了以下总结。

TestNg主要用于单元测试和集成测试,它涵盖了 JUnit4 的全部功能,并且在参数化测试、依赖测试以及套件测试(组)方面功能上更加强大。那么TestNg与Junit4具体有哪些区别呢?

下面表中概括了JUnit 4和TestNG之间的功能比较。如下图所示

image

注释/注解支持在JUnit 4和TestNG中是非常类似的。

特点 JUnit 4 TestNG
测试注释 @Test @Test
在套件中的所有测试运行之前运行 - @BeforeSuite
在套件中的所有测试运行之后运行 - @AfterSuite
测试之前运行 - @BeforeTest
测试之后运行 - @AfterTest
在调用属于任何这些组的第一个测试方法之前运行 - @BeforeGroups
在调用属于任何这些组的第一个测试方法之后运行 - @AfterGroups
在调用当前类的第一个测试方法之前运行 @BeforeClass @BeforeClass
在调用当前类的第一个测试方法之后运行 @AfterClass @AfterClass
在每个测试方法之前运行 @Before @BeforeMethod
在每个测试方法之后运行 @After @AfterMethod
忽略测试 @ignore @Test(enbale=false)
预期的异常 @Test(expected = ArithmeticException.class) @Test(expectedExceptions = ArithmeticException.class)
超时测试 @Test(timeout = 1000) @Test(timeOut = 1000)

1.在Junit 4 中,在方法上使用@BeforeClass和@AfterClass时,那么该测试方法则必须是静态方法。TestNG 被@BeforeClass 和@AfterClass注释的方法可以不写成static方法。

2. 在JUnit 4中,注释命名约定有点混乱,例如“Before”,“After”和“Expected”,我们并不真正了解“Before”和“After”之前的内容,以及要测试中的“预期” 方法。TestiNG更容易理解,它使用类似“BeforeMethod”,“AfterMethod”和“ExpectedException”就很明了。

3.Junit4做忽略测试时,用的是@Ingore,而TestNG则是在@Test注解上添加参数,如:@Test(enabled = false)

4.“超时测试”表示如果单元测试所花费的时间超过指定的毫秒数,则测试将会终止,并将其标记为失败,此功能在JUnit 4和TestNG中均可实现。

Junit4做超时测试时,使用如:@Test(timeout=100)其中o是小写。而TestNG使用如:@Test(timeOut=100)其中O是小写

5.“异常测试”是指从单元测试中抛出的异常。

Junit4做异常测试时,使用如:@Test(expected = ArithmeticException.class) 。

@Test(expected = ArithmeticException.class)
public void divisionWithException() {
  int i = 1/0;
}
image.gif

而TestNg使用如:@Test(expectedExceptions = ArithmeticException.class)

@Test(expectedExceptions = ArithmeticException.class)
public void divisionWithException() {
  int i = 1/0;
}
image.gif

6.套件测试(几个单元测试案例,组合成一个模块,再运行)

JUnit 4中 @RunWith 和 @Suite注解被用于执行套件测试。例如:下面的代码是所展示的是在JunitTest5被执行之后需要JunitTest1 和 JunitTest2也一起执行。所有的声明需要在类内部完成。

@RunWith(Suite.class)
@Suite.SuiteClasses({
    JunitTest1.class,
    JunitTest2.class
})
public class JunitTest5 {
}

TestNG中执行套件测试是使用XML文件配置的方式来做。例如:下面的 XML 的文件可以使得TestNGTest1和TestNGTest2一起执行。

<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd">
<suite name="My test suite">
  <test name="testing">
    <classes>
       <classname="com.fsecure.demo.testng.TestNGTest1"/>
       <classname="com.fsecure.demo.testng.TestNGTest2"/>
    </classes>
  </test>
</suite>

TestNG使用了组的概念,每个方法都可以被分配到一个组里面,可以根据功能特性来分组。
捆绑几个单元测试并一起运行. 下面是一个有四个方法的类,三个组(A1,A2和A3)

public class TestNgGroups {
    @Test(groups = "A1")
    public void test1(){
        System.out.println("this is test1 ---------------");
    }
    @Test(groups = "A2")
    public void test2(){
        System.out.println("this is test2 ---------------");
    }
    @Test(groups = "A1")
    public void test3(){
        System.out.println("this is test3 ---------------");
    }
    @Test(groups = "A3")
    public void test4(){
        System.out.println("this is test4 ---------------");
    }
}

下面XML文件定义了一个只是执行A1的组的单元测试:

<?xml version="1.0" encoding="utf-8" ?>
<suite name="testproj" parallel="false">
    <test name="testDemo1">
        <groups>
            <run>
                <include name="A1"/>
            </run>
        </groups>
     <classes>
         <class name="testng.TestNgGroups"></class>
     </classes>
    </test>
</suite>

7.参数化测试(即给单元测试用例传多个参数值)

JUnit 4中使用@RunWith 和 @Parameter 注解用于为单元测试提供参数值,@Parameters必须返回 List,参数将会被作为参数传给类的构造函数。它在使用上有许多的限制;使用时必须遵循 JUnit 的方式去声明参数,参数必须通过构造函数的参数去初始化类的成员来用于测试。返回的参数类型必须是List [],数据已经被限定为String或者是一个原始值。

@RunWith(value = Parameterized.class)
publicclassJunitTest6 {

     privateintnumber;

     publicJunitTest6(intnumber) {
        this.number = number;
     }

     @Parameters
     publicstaticCollection<Object[]> data() {
       Object[][] data = newObject[][] { { 1}, { 2}, { 3}, { 4} };
       returnArrays.asList(data);
     }

     @Test
     publicvoidpushTest() {
       System.out.println("Parameterized Number is : "+ number);
     }
}

TestNG使用XML文件或者@DataProvider注解来给测试提供参数。
1)、XML文件配置参数化测试时,只需要在方法上声明@Parameters注解,参数的数据将由 TestNG 的 XML 配置文件提供。之后,就可以使用不同的数据集甚至是不同的结果集来重用一个测试用例。

@Parameters({"param1"})
@Test
public void paramterTest(String param1){
    System.out.println("\n---------------"+param1);
}

XML 文件

<?xml version="1.0" encoding="utf-8" ?>
<suite name="testngpro" parallel="tests" thread-count="1">
    <parameter name="param1" value="http://127.0.0.1:4723/wd/hub" />
    <test name="testDemo1">
     <classes>
         <class name="testng.TestNgDataprovider"></class>
     </classes>
    </test>
</suite>

注意:直接运行TestNgDataprovider类,会报错(如下)。需要运行testng.xml才可以。

Parameter &apos;param1&apos; is required by @Test on method paramterTest but has not been marked @Optional or defined
in C:\Users\ccc\.IntelliJIdea2017.3\system\temp-testng-customsuite.xml
    at org.testng.internal.Parameters.createParams(Parameters.java:290)
    at org.testng.internal.Parameters.createParametersForMethod(Parameters.java:359)
    at org.testng.internal.Parameters.createParameters(Parameters.java:620)
    at org.testng.internal.Parameters.handleParameters(Parameters.java:769)
    at org.testng.internal.ParameterHandler.handleParameters(ParameterHandler.java:49)
    at org.testng.internal.ParameterHandler.createParameters(ParameterHandler.java:37)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:914)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at org.testng.TestRunner.privateRun(TestRunner.java:648)
    at org.testng.TestRunner.run(TestRunner.java:505)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
    at org.testng.SuiteRunner.run(SuiteRunner.java:364)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1187)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1116)
    at org.testng.TestNG.runSuites(TestNG.java:1028)
    at org.testng.TestNG.run(TestNG.java:996)
    at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
    at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)

2)、@DataProvider 注解做参数化测试
@ DataProvider的注解,可以更好的把复杂的参数类型映射到一个测试方法上
@DataProvider 可以使用 Vector, String 或者 Integer 类型的值作为参数

public class TestNgDataprovider {
    @Test(dataProvider = "userData")
    public void test(Class clazz, String[] str) {
        System.out.println(clazz + "-------------" + str[0]);
        System.out.println(clazz + "-------------" + str[1]);
    }

    @DataProvider(name = "userData")
    public Object[][] data() {
        Object[][] objects = new Object[][]{
                {Vector.class, new String[]{"java.util.Arrays", "java.util.List"}},
                {String.class, new String[]{"this is my str", "this is my pp"}},
                {Integer.class, new String[]{"123", "345"}},
                {Float.class, new String[]{"12.45f", "33.11f"}}};
        return objects;
    }
}

输出:

class java.util.Vector-------------java.util.Arrays
class java.util.Vector-------------java.util.List
class java.lang.String-------------this is my str
class java.lang.String-------------this is my pp
class java.lang.Integer-------------123
class java.lang.Integer-------------345
class java.lang.Float-------------12.45f
class java.lang.Float-------------33.11f

@DataProvider 作为对象的参数

@Test(dataProvider = "Data-Provider-Function")
public void parameterIntTest(TestNGTest clzz) {
   System.out.println("Parameterized Number is : "+ clzz.getMsg());
   System.out.println("Parameterized Number is : "+ clzz.getNumber());
} 
//This function will provide the patameter data
@DataProvider(name = "Data-Provider-Function")
public Object[][] parameterIntTestProvider() {

    TestNGTestobj = new TestNGTest();
    obj.setMsg("Hello");
    obj.setNumber(123);

    returnnewObject[][]{
        {obj}
    };
}

8.依赖测试,测试的方法是有依赖的,也就是要执行的的方法在执行之前需要执行的部分。如果依赖的方法出现错误,所有的子测试都会被忽略,不会被标记为失败

JUnit4 框架主要聚焦于测试的隔离,暂时还不支持这个特性。TestNG使用dependOnMethods、dependsOnGroups 来实现了依赖测试的功能。

依赖测试方法:如果method1()成功执行,那么method2()也将被执行,否则method2()将会被忽略。

@Test
public  void  method1() {
   System.out.println("This is method 1");
}

@Test(dependsOnMethods={"method1"})
public  void  method2() {
    System.out.println("This is method 2");
}

依赖群组:

    @Test(groups = { "init.1" })
    public void test1() {
    }
    @Test(groups = { "init.2" })
    public void test2() {
    }
    @Test(dependsOnGroups = { "init.*" })
    public void test2() { 
    }

9、并发测试
Junit单元测试不支持多线程测试,TestNg使用threadPoolSize用来指明线程池的大小。
以下例子,并发数为5,可用线程数3。

public class TestNgThreadPoolSize {
    @Test(threadPoolSize = 3,invocationCount = 5)
    public void threadPool(){
        System.out.println("Thread ----------"+Thread.currentThread().getName());
    }
}

输出:

Thread ----------TestNG-methods-3
Thread ----------TestNG-methods-2
Thread ----------TestNG-methods-2
Thread ----------TestNG-methods-3
Thread ----------TestNG-methods-1

使用testng配置文件并发测试
1)、Parallel=”methods”的意思是指TestNG会将method作为并发的元子单位,即每个method运行在自己的thread中

public class TestNgThreadPoolSize1 {
    @Test
    public void threadPool_A(){
        System.out.println();
        System.out.println("Thread A----------"+Thread.currentThread().getId());
    }
    @Test
    public void threadPool_B(){
        System.out.println("Thread B----------"+Thread.currentThread().getId());
    }
    @Test
    public void threadPool_C(){
        System.out.println("Thread C----------"+Thread.currentThread().getId());
    }
    @Test
    public void threadPool_D(){
        System.out.println("Thread D----------"+Thread.currentThread().getId());
    }
    @Test
    public void threadPool_E(){
        System.out.println("Thread E----------"+Thread.currentThread().getId());
    }
}

testng.xml配置:

<?xml version="1.0" encoding="utf-8" ?>
<suite name="testngpro" parallel="methods" thread-count="2">
    <parameter name="param1" value="http://127.0.0.1:4723/wd/hub" />
    <test name="testDemo1">
     <classes>
         <class name="testng.TestNgThreadPoolSize1"></class>
     </classes>
    </test>
</suite>

因为parallel=”methods”,所以每个method都有自己的thread,故输出:

Thread A----------1
Thread B----------1
Thread C----------1
Thread D----------1
Thread E----------1

2)、parallel=”tests”,则指会将test 作为并发的元子单位

总结

在考虑所有功能比较之后,建议使用TestNG作为Java项目的核心单元测试框架,因为TestNG在参数化测试,依赖测试和套件测试(分组概念)方面更加突出。 TestNG用于高级测试和复杂集成测试。 它的灵活性对于大型测试套件尤其有用。 此外,TestNG还涵盖了整个核心的JUnit4功能。这样说来,好像也没有理由使用JUnit了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269