最近在学习接口测试和单元测试,这里有两款基础得测试框架TestNG和Junit5,我先学习了TestNG,这里作为记录

引入TestNG

Maven

<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.5</version>
    <scope>test</scope>
</dependency>

Gradle

testImplementation("org.testng:testng:7.5")

基础注解 @Test @BeforeMethod @AfterMethod @BeforeClass @BeforeSuite @AfterSuite

这里和Junit一样使用的是@Test注解,也有和Junit一样的方法前执行@Before和@AfterMethod方法


public class BasicAnnotation {
  @Test
  public void testCase() {
    System.out.println("测试用例1");
  }

  @BeforeMethod
  public void beforeMethod() {
    System.out.println("在测试方法之前运行");
  }

  @AfterMethod
  public void afterMethod() {
    System.out.println("在测试方法之后运行");
  }

  @Test
  public void testCase2() {
    System.out.println("测试用例2");
  }

  @BeforeClass
  public void beforeClass() {
    System.out.println("在测试类之前运行");
  }

  @BeforeSuite
  public void beforeSuite() {
    System.out.println("在测试套件之前运行");
  }

  @AfterSuite
  public void afterSuite() {
    System.out.println("在测试套件之后运行");
  }

  @AfterClass
  public void afterClass() {
    System.out.println("在测试类之后运行");
  }
}

运行顺序如下
@Test
运行结果如下
image-1651167697676

套件测试

这里我们新建几个测试类
两个普通的测试类

public class LoginTest {
  @Test
  public void loginTaoBao() {
    System.out.println("淘宝登录成功");
  }
}
public class PayTest {
  @Test
  public void paySuccess() {
    System.out.println("支付宝支付成功");
  }
}

套件配置类

public class SuiteConfig {
  @BeforeSuite
  public void beforeSuite() {
    System.out.println("beforeSuite");
  }
  @AfterSuite
  public void afterSuite(){
    System.out.println("afterSuite");
  }

  @BeforeTest
  public void beforeTest() {
    System.out.println("beforeTest");
  }

  @AfterTest
  public void afterTest() {
    System.out.println("afterTest");
  }
}

我们再编写一个xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="test">
  <test name="login">
    <classes>
      <class name="com.example.unittestdemo.suite.SuiteConfig"/>
      <class name="com.example.unittestdemo.suite.LoginTest"/>
    </classes>
  </test>
  <test name="pay">
    <classes>
      <class name="com.example.unittestdemo.suite.SuiteConfig"/>
      <class name="com.example.unittestdemo.suite.PayTest"/>
    </classes>
  </test>
</suite>

这里我们在xml文件中运行这个文件
image-1651168483096
运行结果
image-1651168751631

忽略测试

这里使用@Test的属性来实现忽略测试,即在整体测试中忽略掉这些测试

public class IgnoreTest {
  @Test
  public void ignoreTest() {
    System.out.println("ignoreTest");
  }

  @Test(enabled = false)
  public void ignoreTest2() {
    System.out.println("ignoreTest2");
  }

  @Test(enabled = true)
  public void ignoreTest3() {
    System.out.println("ignoreTest3");
  }
}

运行结果,这个是比较简单的
image-1651169089577

分组测试

分组测试,即通过将测试用例分组 然后根据组名进行测试
这里先写根据方法分组的

方法分组

public class GroupOnMethod {
  @Test(groups = "server")
  public void test1() {
    System.out.println("这是服务端组的测试方法一");
  }

  @Test(groups = "server")
  public void test2() {
    System.out.println("这是服务端组的测试方法二");
  }

  @Test(groups = "client")
  public void test3() {
    System.out.println("这是客户端组的测试方法1");
  }

  @Test(groups = "client")
  public void test4() {
    System.out.println("这是客户端组的测试方法2");
  }

  @BeforeGroups("server")
  public void beforeGroups() {
    System.out.println("这是服务端组运行之前运行的方法");
  }

  @AfterGroups("server")
  public void afterGroups() {
    System.out.println("这是服务端组运行之后运行的方法");
  }
}

这样就可以将方法进行分组测试
image-1651169775152

组分组

接下来 测试根据类进行分组的测试用例,即根据不同的类进行分组,从而进行分组测试

  1. 测试类1
    @Test(groups = "stu")
    public class GroupOnClass1 {
    public void stu1(){
        System.out.println("GroupsOnClass1中的stu1运行");
    }
      public void stu2(){
        System.out.println("GroupsOnClass1中的stu2运行");
      }
    }
    
  2. 测试类2
    @Test(groups = "stu")
      public class GroupOnClass2 {
        public void stu1(){
          System.out.println("GroupsOnClass2中的stu1运行");
        }
        public void stu2(){
          System.out.println("GroupsOnClass2中的stu2运行");
        }
      }
    
  3. 测试类3
    @Test(groups = "teacher1")
    public class GroupOnClass3 {
      public void stu1() {
        System.out.println("GroupsOnClass3中的teacher1运行");
      }
    
      public void stu2() {
        System.out.println("GroupsOnClass3中的teacher2运行");
      }
    }
    

在写完三个测试类后,我们依旧要编写xml文件
```xml









我们点击运行,则上述的xml文件,会将三个测试类的方法都运行一次,顺序也是按照xml定义的顺序执行的
![image-1651231348872](/upload/2022/04/image-1651231348872.png)

随后我们填入<groups></groups>项,再看看运行的结果
xml如下
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="suitename">
<test name="onlyRunStu">
  <groups>
    <run>
      <include name="stu"/>
    </run>
  </groups>
  <classes>
    <class name="com.example.unittestdemo.groups.GroupOnClass1"/>
    <class name="com.example.unittestdemo.groups.GroupOnClass2"/>
    <class name="com.example.unittestdemo.groups.GroupOnClass3"/>
  </classes>
</test>
</suite>

这里我们一共放了三个类,但指定了运行组为stu.运行一下查看结果
image-1651231608933
结果只有stu组的类的方法运行了,符合预期

异常测试

异常测试即为,运行方法需抛出与期望值相同的异常,否则,测试失败
这里写两个异常测试的Demo

public class ExceptedException {
  /** 什么时候会用到异常测试 在我们期望结果为某一个异常的时候 比如我们:传入了某些不合法的参数,程序抛出的异常 也就是说我们预期结果就是这个异常 */
  // 这是一测试结果会失败的异常测试
  @Test(expectedExceptions = RuntimeException.class)
  public void runTimeExceptionFailed() {
    System.out.println("这是一测试结果会失败的异常测试");
  }

  @Test(expectedExceptions = RuntimeException.class)
  public void runTimeExceptionSuccess() {
    System.out.println("这是一测试结果会成功的异常测试");
    throw new RuntimeException();
  }
}

image-1651233332855
如果抛出的异常或者不抛出异常,而@Test中又有expectedExceptions 项,则会抛出测试失败
如果抛出的异常与expectedExceptions 中的异常相同,则测试正常运行

依赖测试

顾名思义,依赖测试即一个测试用例依赖另一个用例
首先先写一个正常运行的测试用例

public class DependTest {
  @Test
  public void test1() {
    System.out.println("test1 run");
    
  }

  @Test(dependsOnMethods = "test1")
  public void test2() {
    System.out.println("test2 run");
  }
}

image-1651233660023

如果这个时候test1()发生异常,则我们继续观察结果如何

public class DependTest {
  @Test
  public void test1() {
    System.out.println("test1 run");
    throw new RuntimeException("test1 exception");
  }

  @Test(dependsOnMethods = "test1")
  public void test2() {
    System.out.println("test2 run");
  }
}

image-1651233783911
这个时候由于test1异常的原因,所以依赖它的test2()被忽略了

参数测试

有时我们需要提供特定的参数供测试方法进行使用
如果要对指定的测试方法进行参数测试我们需要这样编写测试用例

public class ParameterTest {
  @Test
  @Parameters({"name", "age"})
  public void paramTest1(String name, int age) {
    System.out.println("name:" + name + "age:" + age);
  }
}

这里我们需要提供name和age两个参数进行测试

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="parameter">
  <test name="parameter">
    <classes>
      <parameter name="name" value="zhangsan"></parameter>
      <parameter name="age" value="10"></parameter>
      <class name="com.example.unittestdemo.parameter.ParameterTest">

      </class>
    </classes>
  </test>
</suite>

注:这里我们需要在xml文件里进行测试用例的运行,如果在上面的类中运行,则会报错
image-1651234614130

有人可能比较讨厌xml(比如我),这里我们使用Java类配置的情况,来对测试用例进行

public class DataProviderTest {
  @Test(dataProvider = "data")
  public void testDataProvider(String name, int age) {
    System.out.println("name:" + name + " age:" + age);
  }

  @DataProvider(name = "data")
  public Object[][] providerData() {
    return new Object[][] {
      {"张三", 20},
      {"李四", 30}
    };
  }

运行结果
image-1651235004521
通过测试结果,我们可以得知测试数据提供了两组,测试进行了两次

我们也可以通过一个数据提供者为多个测试提供参数数据

  @Test(dataProvider = "methodData")
public void test1(String name, int age) {
  System.out.println("test1 方法执行" + name + " " + age);
}

@Test(dataProvider = "methodData")
public void test2(String name, int age) {
  System.out.println("test2 方法执行" + name + " " + age);
}

@DataProvider(name = "methodData")
public Object[][] methodDataTest(Method method) {
  Object[][] result = null;
  if (method.getName().equals("test1")) {
    result = new Object[][] {{"张三", 20}};
  } else if (method.getName().equals("test2")) {
    result = new Object[][] {{"李四", 30}};
  }
  return result;
}

超时测试

TestNg也支持超时测试,这里我们简单写一个实例

public class TimeOutTest {
@Test(timeOut = 3000)
  public void testTimeOut() throws InterruptedException {
    Thread.sleep(2000);
  }

  @Test(timeOut = 2000)
  public void testTimeOut2() throws InterruptedException {

    Thread.sleep(3000);
  }
}

如果用例执行的时间超过,指定的时间则,用例会测试失败

多线程测试

多线程测试是将不相关或者不想依赖用例进行多线程运行减少测试时间

public class MultiThreadOnAnnotation {

  /**
   * 多线程测试,没有关联的用例可以使用多线程,减少执行时间
   * 以下演示3个线程同时运行,共执行10次
   */
  @Test(invocationCount = 10,threadPoolSize = 3)
  public void test() {

    System.out.println(Thread.currentThread().getId());
  }
}

如果使用xml则有一个限制 无法指定线程池的数量

Q.E.D.