TestNG框架学习笔记

admin 2024年1月24日11:11:10评论11 views字数 18209阅读60分41秒阅读模式

1. 介绍 ]

官方网站
GitHub地址

1.1 什么是 TestNG?

TestNG(Test Next Generation)是一个用于测试Java程序的测试框架。主要用于自动化测试,包括单元测试、集成测试、端到端测试等。虽然它被广泛用于白盒测试(例如单元测试),但并不局限于白盒测试。它支持并发、参数化测试、测试组织和测试报告生成。通过 TestNG 可以更灵活、高效地进行测试,并获得详尽的测试结果报告。

本文主要是探索使用 TestNG 框架进行白盒安全测试。

1.2 Java 版本要求

  • TestNG <= v7.5:JDK 8。

  • TestNG >= v7.6.0:JDK 11 或更高版本。

1.3 TestNG 编写和执行流程

使用 TestNG 编写和执行测试的基本流程如下:

  1. 导入依赖: 在项目中使用 Maven 或其他构建工具,导入 TestNG 的依赖,或下载 TestNG JAR 文件来导入 TestNG

  2. 编写测试类: 创建测试类,并使用TestNG的注解为测试方法配置测试环境

  3. 创建配置文件: 使用 Maven 构建工具的情况下,在项目中创建testng.xml配置文件,用于配置测试套件的执行方式、参数等。使用 Apache Ant 构建工具的情况下,在项目中创建build.xml配置文件,用于定义项目的构建和编译过程,包括编译源代码、运行测试等

  4. 运行测试: 使用TestNG运行测试。可以通过命令行、IDE插件(例如在IntelliJ IDEA或Eclipse中运行TestNG测试),或者通过构建工具(如Maven、Gradle或Apache Ant)来执行测试。

2. 导入依赖 ]

2.1 直接导入 JAR 文件

直接在快照网站下载jar文件,放入项目 lib 目录下:
TestNG框架学习笔记

2.2 使用 Maven 构建工具

如果使用 Maven 作为项目构建工具,可以在 pom.xml 文件中添加以下依赖配置:

<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version> <!-- 使用最新版本 -->
<scope>test</scope>
</dependency>

2.3 使用 Apache Ant 构建工具

在 Apache Ant 构建工具中,通常使用 Ivy 作为依赖管理工具来导入依赖。在项目的根目录创建一个 ivy.xml 文件:

<ivy-module version="2.0">
<info organisation="your-organization" module="your-project" />

<dependencies>
<dependency org="org.testng" name="testng" rev="7.4.0" />
<!-- 添加其他依赖项 -->
</dependencies>
</ivy-module>

2.4 使用 Gradle 构建工具

在 build.gradle 文件添加依赖配置:

plugins {
id 'java'
}

repositories {
mavenCentral()
}

dependencies {
testImplementation 'org.testng:testng:7.4.0'
// 添加其他依赖项
}

test {
useTestNG()
}

3. 编写测试类 ]

3.1 注解

注解(Annotation)是 Java 5 引入的一种元数据形式,它为程序元素(类、方法、字段等)添加附加信息。注解以 @ 符号开头,如 @Test@Override 等。注解提供了对代码进行元数据标记的方式,可以用于配置、文档生成、运行时处理等。Java中的类、方法、变量、参数、包都可以被注解。

3.1.1 @Test 注解

@Test 注解用于标记一个测试方法。测试方法是实际执行测试的地方。

import org.testng.annotations.Test;

public class MyTest {
@Test
public void testMethod() {
// 这里写你的测试代码
}
}

3.1.2 生命周期注解

这些注解用于控制测试执行的生命周期:
@BeforeTest: 在所有测试方法运行之前执行,一般用来执行一些初始化操作。
@AfterTest: 在所有测试方法运行之后执行,一般用来执行一些清理操作。
@BeforeClass: 在测试类中的所有测试方法运行之前执行,这个方法在整个测试类中只会执行一次,通常用于设置测试类级别的一些预备操作,例如初始化资源、建立连接等。
@AfterClass: 在测试类中的所有测试方法运行之后执行,同样在整个测试类中只会执行一次,通常用于进行一些清理工作或资源释放。
@BeforeMethod: 在每个测试方法运行之前执行。
@AfterMethod: 在每个测试方法运行之后执行。

import org.testng.annotations.*;

public class MyTest {
@BeforeTest
public void setupBeforeTest() {
// 执行一些初始化操作
}

@AfterTest
public void cleanupAfterTest() {
// 执行一些清理操作
}

@BeforeClass
public void setupBeforeClass() {
// 这里可以进行一些初始化操作,例如设置连接,准备测试数据等
}

@AfterClass
public void cleanupAfterClass() {
// 这里可以进行一些清理工作,例如关闭连接,释放资源等
}

@BeforeMethod
public void setupBeforeMethod() {
// 在每个测试方法运行之前执行
}

@AfterMethod
public void cleanupAfterMethod() {
// 在每个测试方法运行之后执行
}

@Test
public void testMethod() {
// 这里写你的测试代码
}
}

3.1.3 @DataProvider 注解

@DataProvider 注解用于提供测试方法的参数。可以在同一个测试方法中运行多组数据。

import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;

public class ParameterizedTest {
@Test(dataProvider = "testData")
public void testMethod(String param1, int param2) {
// 使用参数运行测试
}

@DataProvider(name = "testData")
public Object[][] testData() {
return new Object[][] {
{"value1", 1},
{"value2", 2},
// 添加更多测试数据
};
}
}

3.1.4 依赖注解

通过 dependsOnMethods 和 dependsOnGroups 属性可以定义测试方法之间的依赖关系。

import org.testng.annotations.Test;

public class DependencyTest {
@Test
public void setup() {
// 设置测试环境
}

@Test(dependsOnMethods = "setup")
public void testMethod() {
// 测试代码
}
}

3.1.5 @Test(groups = "groupName") 注解

分组可以帮助你组织和运行测试。可以通过分组运行指定的测试。

import org.testng.annotations.Test;

public class GroupTest {
@Test(groups = "group1")
public void testMethod1() {
// 测试代码属于 group1
}

@Test(groups = "group2")
public void testMethod2() {
// 测试代码属于 group2
}
}

3.1.6 并发注解

通过 @Test(threadPoolSize = n, invocationCount = m) 注解可以实现并发测试,让相同的测试方法运行多次或同时运行。

import org.testng.annotations.Test;

public class ParallelTest {
@Test(threadPoolSize = 5, invocationCount = 10)
public void testMethod() {
// 并发运行的测试代码
}
}

3.1.7 @Ignore 注解

在测试开发中,有时候你可能会遇到一些特殊情况,需要暂时禁用或忽略某些测试方法。这时使用 @Ignore 注解可以达到以下目的:

  • 临时排除测试:当某个测试方法因为某个 bug 或其他原因导致无法通过,但你又不希望删除这个测试方法,可以使用 @Ignore 注解进行标记,暂时排除它,使其不参与当前测试运行。

  • 跳过不稳定的测试:有些测试可能对外部环境或资源有依赖,导致它们在不同的执行环境中表现不稳定。在这种情况下,你可以通过 @Ignore 注解将这些不稳定的测试方法排除,以避免测试结果的误导。

  • 跳过不适用的测试:在某些情况下,测试方法可能仅适用于特定的场景或配置。如果当前运行环境不满足这些条件,你可以使用 @Ignore 注解来跳过这些不适用的测试。

@Test
@Ignore("这个测试方法有一个已知的 bug,正在修复中")
public void ignoredTestWithReason() {
// 测试逻辑
}

3.2 监听器

在TestNG中,监听器是一种机制,允许你在测试执行的不同生命周期事件中插入自定义行为。监听器可以捕获测试生命周期中的不同事件,例如测试开始、测试结束、测试方法执行前后等。通过使用监听器,可以在测试执行过程中执行额外的操作或记录日志,以便更好地了解测试的执行情况。

3.2.1 内置监听器

TestNG 提供了一些内置的监听器,同时也支持用户创建自定义的监听器。以下是一些常用的内置监听器:

  • IInvokedMethodListener: 用于监听每个测试方法的调用。

  • ISuiteListener: 用于监听整个测试套件的启动和结束。

  • ITestListener: 用于监听单个测试的启动和结束。

  • IReporter: 用于生成报告。

  • IAnnotationTransformer: 用于在运行时修改注解。

3.2.2 自定义监听器

可以实现 TestNG 提供的监听器接口,创建自定义监听器来处理测试过程中的事件。

import org.testng.ITestListener;
import org.testng.ITestResult;

public class MyTestListener implements ITestListener {
// 实现 ITestListener 接口的方法
}

在 testng.xml 文件中注册监听器:

<listeners>
<listener class-name="path.to.MyTestListener" />
</listeners>

3.3 测试结果

在测试中,通常使用断言(Assertion)来验证测试的预期结果。断言是一种用于检查测试中特定条件的机制,如果测试完成时没有引发任何异常,或者引发了预期的异常,则测试被视为成功。如果引发了预期外的异常,从而表明测试失败。这样,测试结果中就能够体现符合预期和不符合预期的情况。

TestNG 提供了一系列的断言方法,最常用的是 assertEquals,它比较两个值是否相等。例如:

import org.testng.Assert;
import org.testng.annotations.Test;

public class ExampleTest {

@Test
public void testAddition() {
int result = add(2, 3);
Assert.assertEquals(result, 5, "加法不正确");
}

@Test
public void testSubtraction() {
int result = subtract(5, 3);
Assert.assertEquals(result, 2, "减法不正确");
}

private int add(int a, int b) {
return a + b;
}

private int subtract(int a, int b) {
return a - b;
}
}

在这个例子中,Assert.assertEquals(result, 5, "Addition is incorrect"); 语句表示预期 result 的值应该是 5,如果不是,就会引发断言失败的异常,并且在报告中会显示 "加法不正确"。

3.4 编写测试用例

在 TestNG 中,测试用例(Test Case)是通过 Java 类中的测试方法(Test Method)来定义的。这些测试方法通过 @Test 注解进行标记。以下是一个简单的示例:

import org.testng.Assert;
import org.testng.annotations.*;

public class BasicTestNGExamples {

// 在测试类中的所有测试方法运行之前执行
@BeforeClass
public void setUpClass() {
System.out.println("执行所有测试方法前的准备工作");
}

// 在测试类中的所有测试方法运行之后执行
@AfterClass
public void tearDownClass() {
System.out.println("执行所有测试方法后的清理工作");
}

// 在每个测试方法运行之前执行
@BeforeMethod
public void setUp() {
System.out.println("执行每个测试方法前的准备工作");
}

// 在每个测试方法运行之后执行
@AfterMethod
public void tearDown() {
System.out.println("执行每个测试方法后的清理工作");
}

// 测试方法1
@Test(priority = 1, description = "这是第一个测试方法")
public void testMethod1() {
System.out.println("执行测试方法1");
Assert.assertTrue(true, "断言失败信息");
}

// 测试方法2
@Test(priority = 2, description = "这是第二个测试方法")
@Parameters("input")
public void testMethod2(String input) {
System.out.println("执行测试方法2");
Assert.assertEquals(input, "Hello", "断言失败信息");
}

// 忽略测试方法
@Test(priority = 3, description = "这个方法会被忽略")
@Ignore("忽略的原因")
public void ignoredTest() {
System.out.println("这个方法不会被执行");
}

// 依赖其他测试方法的测试
@Test(priority = 4, description = "依赖其他测试方法")
@DependsOnMethods("testMethod2")
public void dependentTest() {
System.out.println("这个方法依赖于 testMethod2");
}

// 参数化测试
@Test(priority = 5, description = "参数化测试", dataProvider = "dataProvider")
public void parameterizedTest(String input1, String input2, int expectedResult) {
System.out.println("参数1: " + input1 + ", 参数2: " + input2);
int result = Integer.parseInt(input1) + Integer.parseInt(input2);
Assert.assertEquals(result, expectedResult, "断言失败信息");
}

// 数据提供者方法
@DataProvider(name = "dataProvider")
public Object[][] provideData() {
return new Object[][]{
{"1", "2", 3}, // 第一组测试数据
{"-2", "5", 3}, // 第二组
{"0", "0", 0} // 第三组
};
}
}

这个示例涵盖了一些基础的 TestNG 注解和场景,包括:

  • @BeforeClass 和 @AfterClass 注解的使用。

  • @BeforeMethod 和 @AfterMethod 注解的使用。

  • @Test 注解的基本用法,包括设置测试方法的优先级、描述、依赖关系等。

  • @Ignore 注解用于忽略某个测试方法。

  • @DependsOnMethods 注解用于定义测试方法之间的依赖关系。

  • 数据参数化的实现,使用 @DataProvider 注解提供测试数据。

4. 创建配置文件 ]

4.1 使用 Maven

在项目的根目录,创建一个名为 testng.xml 的文件。文件内容格式如下:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="MyTestSuite">
<test name="MyTestClass">
<classes>
<!-- 指定测试类 -->
<class name="your.package.name.TestClass" />
</classes>
</test>
</suite>
  • <suite> 元素用于定义整个测试套件,必须包含 name 属性,该属性表示测试套件的名称,是为了标识测试套件的名称而设定的,它可以是任何字符串,只要它符合 XML 的命名规则即可。<suite> 元素内可以包含多个 <test> 元素。

  • <test> 元素用于定义一个具体的测试,必须包含 name 属性,表示测试的名称。它可以包含多个 <classes><packages><methods> 或 <parameter> 元素,用于指定包含在该测试中的测试类、包、方法或参数。

请确保将 "your.package.name" 替换为 TestClass 所在的实际包名。如果您想配置 TestNG 生成测试报告,可以在 <suite> 元素中添加 <listeners> 元素,并指定相应的报告生成器。以下是一个示例:

<suite name="MyTestSuite">
<!-- ... 其他配置 ... -->

<!-- 设置生成测试报告 -->
<listeners>
<listener class-name="org.testng.reporters.EmailableReporter"/>
<listener class-name="org.testng.reporters.JUnitReportReporter"/>
<listener class-name="org.testng.reporters.TestHTMLReporter"/>
<listener class-name="org.testng.reporters.XMLReporter"/>
</listeners>

<!-- 设置测试报告输出目录 -->
<parameter name="output-directory" value="test-output" />
</suite>

4.2 使用 Apache Ant

Apache Ant 使用 XML 文件作为配置文件,通常命名为 build.xml。以下是一个简单的示例,演示了一个包含编译、运行测试和生成报告等任务的 build.xml 文件:

<project name="MyProject" default="run-tests" basedir=".">

<!-- 定义属性 -->
<property name="src.dir" value="src" />
<property name="build.dir" value="build" />
<property name="test.dir" value="test" />
<property name="report.dir" value="test-output" />

<!-- 定义路径 -->
<path id="classpath">
<pathelement location="${build.dir}" />
<pathelement path="${java.class.path}" />
</path>

<!-- 编译源代码 -->
<target name="compile">
<mkdir dir="${build.dir}" />
<javac srcdir="${src.dir}" destdir="${build.dir}" includeantruntime="false">
<classpath refid="classpath" />
</javac>
</target>

<!-- 运行测试 -->
<target name="run-tests" depends="compile">
<testng classpathref="classpath">
<xmlfileset dir="${basedir}" includes="testng.xml" />
</testng>
</target>

<!-- 生成报告 -->
<target name="generate-report" depends="run-tests">
<mkdir dir="${report.dir}" />
<testng-results outputDir="${report.dir}" />
</target>

</project>

在这个示例中,Ant 项目包含三个主要任务:compile、run-tests 和 generate-report。

  • compile 任务用于编译源代码

  • run-tests 任务用于运行测试

  • generate-report 任务用于生成测试报告

这些任务的执行顺序由依赖关系定义

5. 运行测试 ]

5.1 使用 Maven

通过 Maven 插件运行 TestNG 测试。在 Maven 项目中,你可以使用以下命令:

mvn test

5.2 使用 TestNG 的命令行运行器

通过 TestNG 提供的命令行运行器运行测试。例如:

java -cp "path/to/testng.jar:path/to/your/classes" org.testng.TestNG path/to/testng.xml

5.3 使用 Apache Ant

在Apache Ant项目中,编辑好build.xml后执行:

ant runTests

6. 使用实例 ]

6.1 自己写的API登录功能测试

使用 TestNG 对登录功能进行测试,采用参数化测试,不考虑大文件读取。配置 Maven 构建环境:
TestNG框架学习笔记

6.1.1 测试环境

登录地址为: /login
请求方法: POST
数据格式: json
参数: {"Username:"",Password:""}
预期结果:

  1. 登录成功响应200并返回:

    {
    "message": "xxxx",
    "status": "True"
    }
  2. 登录失败响应200并返回:

    {
    "message": "用户名或密码不正确",
    "status": "False"
    }
  3. Json格式错误响应500并返回:

    {
    "message": "XXX",
    "status": "Error"
    }
  4. 其他错误情况未知

6.1.2 编写测试类

使用 Maven 构建项目,编码实现 LoginTest 测试类和测试方法。

6.1.2.1 添加依赖

使用 TestNG 6.14.3 实现测试类,使用 json 解析登录的 json 数据,pom.xml文件内容如下:

<?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>org.example</groupId>
<artifactId>LoginTest</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>
</dependencies>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

</project>

6.1.2.2 编码实现测试类

定义 LoginTest 类,使用user.txt(m行)和pass.txt(n行)文件中的字符串做为登录的参数,使用数据提供器生成 m*n 组测试数据进行测试,LoginTest.java文件内容如下:

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.Assert;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;

public class LoginTest {

// 数据提供器,从user.txt和pass.txt中提供测试数据
@DataProvider(name = "userData")
public Object[][] provideUserData() {
List<Object[]> userDataList = new ArrayList<>();

// 从文件中读取用户名和密码
List<String> usernames = readLinesFromFile("user.txt");
List<String> passwords = readLinesFromFile("pass.txt");

// 组合用户名和密码,形成测试数据
for (String username : usernames) {
for (String password : passwords) {
userDataList.add(new Object[]{username, password});
}
}

return userDataList.toArray(new Object[0][0]);
}

// 测试方法,用于测试登录接口
@Test(dataProvider = "userData", description = "测试登录接口")
public void testLogin(String username, String password) {
String apiUrl = "http://127.0.0.1/login";
String requestBody = "{"Username":"" + username + "", "Password":"" + password + ""}";

try {
// 创建URL对象
URL url = new URL(apiUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);

// 设置请求头,指定 Content-Type 为 application/json
connection.setRequestProperty("Content-Type", "application/json");

// 将请求体写入输出流
try (OutputStream os = connection.getOutputStream()) {
byte[] input = requestBody.getBytes("utf-8");
os.write(input, 0, input.length);
}

// 读取响应
try (BufferedReader br = new BufferedReader(new java.io.InputStreamReader(connection.getInputStream(), "utf-8"))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}

// 解析响应
String status = getStatusFromResponse(response.toString());
String message = getMessageFromResponse(response.toString());

// 验证响应
if ("True".equals(status)) {
Assert.assertTrue(response.toString().contains(""message""), "登录成功响应缺少message字段");
} else if ("False".equals(status)) {
Assert.assertTrue(response.toString().contains(""message""), "登录失败响应缺少message字段");
} else if ("Error".equals(status)) {
Assert.assertTrue(response.toString().contains(""message""), "登录请求发生错误: " + message);
} else {
Assert.fail("未处理的响应情况: " + response.toString());
}
}
} catch (IOException e) {
e.printStackTrace();
Assert.fail("登录请求期间出现异常: " + e.getMessage());
}
}

// 从文件中读取每一行数据
private List<String> readLinesFromFile(String fileName) {
List<String> lines = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return lines;
}

// 从响应中获取状态字段
private String getStatusFromResponse(String response) {
try {
JSONObject jsonResponse = new JSONObject(response);
return jsonResponse.optString("status", "");
} catch (Exception e) {
e.printStackTrace();
return "";
}
}

// 从响应中获取消息字段
private String getMessageFromResponse(String response) {
try {
JSONObject jsonResponse = new JSONObject(response);
return jsonResponse.optString("message", "");
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}

6.1.3 创建配置文件

在项目的根目录,创建一个名为 testng.xml 的文件,内容如下:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="MyTestSuite">
<test name="MyTestClass">
<classes>
<!-- 指定测试类 -->
<class name="LoginTest" />
</classes>
</test>

<!-- 设置生成测试报告 -->
<listeners>
<listener class-name="org.testng.reporters.EmailableReporter"/>
<listener class-name="org.testng.reporters.JUnitReportReporter"/>
<listener class-name="org.testng.reporters.TestHTMLReporter"/>
<listener class-name="org.testng.reporters.XMLReporter"/>
</listeners>

<!-- 设置测试报告输出目录 -->
<parameter name="output-directory" value="test-output" />
</suite>

6.1.4 运行

使用 IEDA 集成 IDE,直接运行即可:
TestNG框架学习笔记

6.1.5 报告输出

报告路径是配置文件中定义的:
TestNG框架学习笔记
TestNG框架学习笔记

6.2 DVWA SQL注入测试

6.2.1 测试环境

漏洞环境使用Dvwa的Low级别测试SQL注入
TestNG框架学习笔记
Payload通过自定义sql.txt实现,内容如下:

'
'or'1'='1
\

6.2.2 编写测试类并运行

代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.testng.Assert;
import org.testng.annotations.Test;

public class DvwaTest {

private String dvwaURL = "http://dvwa.com";
private String sqlInjectionEndpoint = "/vulnerabilities/sqli/";
private String payloadDirectory = "payload";

@Test
public void testSQLInjection() throws IOException {
// 从文件中读取SQL注入payload
List<String> sqlInjectionPayloads = readPayloadsFromFile("sql.txt");

// 构造HTTP客户端
CloseableHttpClient httpClient = HttpClients.createDefault();

// 迭代每个测试输入
for (String sqlInjectionPayload : sqlInjectionPayloads) {
// 构造GET请求
String encodedPayload = urlEncode(sqlInjectionPayload);
String fullUrl = dvwaURL + sqlInjectionEndpoint + "?id=" + encodedPayload + "&Submit=Submit#";
HttpGet httpGet = new HttpGet(fullUrl);

// 执行GET请求
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 读取响应内容
HttpEntity entity = response.getEntity();
String responseBody = readResponse(entity);

// 输出实际响应内容,方便调试
System.out.println("SQL注入Payload: " + sqlInjectionPayload);
System.out.println("实际响应内容: " + responseBody);

// 检查页面是否包含任何数据库的SQL语句报错信息
boolean isVulnerabilityExploited = !responseBody.contains("SQL syntax");

// 断言实际响应内容中不包含SQL报错语句
Assert.assertFalse(responseBody.contains("SQL syntax"), "返回页面包含SQL报错语句:" + responseBody);
System.out.println("测试结果: " + (isVulnerabilityExploited ? "通过" : "不通过"));
System.out.println();
}
}

// 关闭HTTP客户端
httpClient.close();
}

// 从文件中读取多个payload的辅助方法
private List<String> readPayloadsFromFile(String fileName) throws IOException {
String filePath = Paths.get(payloadDirectory, fileName).toString();
return Files.readAllLines(Paths.get(filePath));
}

// URL编码辅助方法
private String urlEncode(String value) throws UnsupportedEncodingException {
return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
}

// 读取响应内容的辅助方法
private String readResponse(HttpEntity entity) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()))) {
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
return result.toString();
}
}
}

存在SQL报错语句时,测试不通过:
TestNG框架学习笔记
不存在SQL报错语句时,测试通过:
TestNG框架学习笔记

7. 疑问解答 ]

7.1 TestNG主要用于测试什么

TestNG(Test Next Generation)是一个用于执行单元测试、集成测试和功能测试的测试框架。它主要用于测试Java应用程序,但也可以用于测试其他类型的应用程序。以下是 TestNG 的一些主要用途:

  • 单元测试:TestNG 支持执行单元测试,确保代码中的各个单元(方法、类等)按照预期工作。这有助于发现和修复代码中的错误,确保每个单元都按照设计进行测试。

  • 集成测试:TestNG 提供了对集成测试的强大支持。在集成测试中,多个单元被组合在一起以验证它们一起工作的方式。这有助于检测系统中组件之间的交互问题。

  • 功能测试:TestNG 支持执行功能测试,确保整个应用程序在各种情况下都能够按照用户需求正常运行。功能测试涉及对应用程序的端到端测试,以确保其功能完整性。

  • 并发测试:TestNG 具有对并发测试的内置支持。这意味着测试可以在多个线程中并行执行,加快测试执行速度。

  • 数据驱动测试:TestNG 支持数据驱动测试,允许在不同的输入数据集上运行相同的测试用例。这对于对不同数据集进行测试和验证应用程序行为很有用。

  • 配置管理:TestNG 提供了注解(Annotations)和配置文件的方式来管理测试用例的配置,例如测试的顺序、依赖关系、测试套件等。

7.2 TestNG是怎么确定测试执行成功、失败或预期外的异常的

TestNG 确定测试执行状态的方式主要基于断言和异常的处理。以下是 TestNG 如何判断测试执行状态的一般流程:

  • 成功(Success):如果在测试方法中的所有断言成功执行,即条件满足,没有抛出 AssertionError 异常,那么 TestNG 将标记该测试为成功。TestNG 的断言方法如 Assert.assertEquals()、Assert.assertTrue() 等用于判断测试的实际结果是否符合预期。

  • 失败(Failure):如果测试方法中的任何断言失败,即条件不满足,抛出了 AssertionError 异常,TestNG 将标记该测试为失败。测试失败意味着测试中的某些期望结果与实际结果不匹配。

  • 预期外的异常(Unexpected Exception):如果测试方法抛出了除了 AssertionError 以外的异常,TestNG 也会将测试标记为失败。这包括在测试方法中抛出的任何未被捕获的异常。

  • 跳过(Skipped):通过 @Test(enabled = false) 或其他一些跳过机制,可以标记测试方法或依赖的测试方法不执行,TestNG 将标记测试为跳过。

  • 超时(Timeout):如果测试方法在规定的时间内没有完成,可以通过 @Test(timeOut = ...) 设置超时时间,TestNG 将标记测试为超时。

还可以通过监听器捕获测试执行期间的各种事件,例如测试开始、测试结束、测试成功、测试失败等。

7.3 TestNG 可以测试和无法测试的适用于API的漏洞类型有哪些?

TestNG可以测试的漏洞类型:

  1. 用户名枚举

  2. 暴力破解

  3. 未授权访问

  4. 敏感数据泄露

  5. 有回显类漏洞,如有回显的SQL注入. 有回显的XSS. 有回显的命令注入

  6. 路径穿越

  7. 任意文件上传/覆盖

  8. 任意文件读取/下载

  9. 参数污染

TestNG无法测试的漏洞类型:

  1. 无回显类漏洞,如基于bool的SQL注入. 基于time的SQL注入. 无回显XSS. 无回显命令注入. SSRF

  2. 需要用户交互的漏洞,如CSRF

  3. 特殊显示的漏洞,如返回文件上传失败但成功上传任意文件

  4. 需要回调请求的漏洞,如XXE. 不安全的反序列化

  5. 逻辑漏洞,如复杂的越权. 复杂的业务流程

  6. 拒绝服务


原文始发于微信公众号(Hack All Sec):TestNG框架学习笔记

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月24日11:11:10
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   TestNG框架学习笔记http://cn-sec.com/archives/2424857.html

发表评论

匿名网友 填写信息