环境搭建及系统部署
VirtualBox安装Centos 7教程
首先先将virtualBox软件安装
如果在安装时,出现下图的这个东西,一定好安装
安装完成之后,打开软件
首先新建虚拟机
之后设置内存大小
创建虚拟硬盘
默认选择
这个也是默认选项
默认选项
之后是磁盘大小,最低不能为10G 40G左右最好
之后点击创建
创建完成之后设置镜像文件
然后选择自己的centos7镜像文件
之后在点击到网络,设置为桥接
然后确定启动就可以了。
注意:
在我们安装成功之后,查看IP的时候,是没有启动enp0s3网卡,所有没有IP地址
使用 iv打开ifcfg- enp0s3网卡的配置文件,然后启用网卡
vi /etc/sysconfig/network-scripts/ifcfg- enp0s3
之后在查看IP就有地址了
Jdk配置
首先先将文件上传到centos7(开始22端口)中
使用tar命令解压
之后创建存储jdk的文件夹
之后将解压的文件移动到刚刚创建的文件夹中
配置环境变量
移动到文件最下面,添加如下语句
之后保存
然后执行让环境变量生效
之后输入命令Java -version
这样Java环境就配置成功
MySQL配置
首先先检测系统是否安装过MySQL(比赛系统一般是没有的,但是以防万一)
返回为空则没有
如果系统自带MySQL,则查找mysql相关文件,将所有相关文件删除
如果没有安装过,则可以忽略此步骤。
卸载系统自带的mariadb
查看系统自带的mariadb
删除
删除ect/my.cnf 注:一定要删除如果没有则不用删除
检测mysql用户组是否存在,不存在则创建
没有任何显示,则不存在
创建mysql用户组
参数 -r : 表示为系统用户
-g: 表示隶属于mysql用户组
安装mysql
首先将压缩包解压到usr/bin目录下,并改名为mysql
配置my.cnf
配置完成之后对my.cnf进行权限赋值
配置文件如下
之后进入mysql根目录,将目录拥有者改为mysql
创建套接字文件所在目录,和配置文件中socket的一致
进行初始化数据库(安装)
--user=mysql:指定运行 mysql_install_db 的用户为 mysql。这将确保生成的文件和目录的所有权归属于 mysql 用户。
--basedir=/usr/local/mysql:指定 MySQL 的安装目录。这个参数告诉 mysql_install_db 在指定的目录下找到 MySQL 的安装文件和配置文件。
--datadir=/usr/local/data:指定 MySQL 的数据目录。这个参数告诉 mysql_install_db 在指定的目录下创建存储数据库文件的目录。你可以将其设置为任何你喜欢的可用的目录。
注:
如果出现这个报错,使用命令perl -v 进行查看是否安装过perl, 如果没有则使用命令
Yum install perl进行安装
安装了perl之后在执行如果在出现这个错误,则使用yum install cpan进行安装
之后在执行,如果最后出现下面的内容,则安装成功。
复制启动脚本到资源目录
将mysqld服务加入到系统服务
出现上图的显示则成功
之后启动服务
到此mysql安装成功
登录,没有密码
登录之后使用命令进行修改root的密码
Tomcat配置
首先将压缩包解压到/usr/local
将解压的文件名称改为Tomcat
之后可以启动来检测是否安装成功
关闭命令
如何查看Tomcat开启还是关闭了
如果是开启则会显示
关闭显示
出现如下信息则安装成功
之后在浏览器中输入IP:8080进行访问Tomcat
如果出现上图问题,那么可能是被防火墙阻止
开放8080端口
输入命令没有错误的话会提示成功
之后重启防火墙,然后再查看开放端口
之后在访问就成功了
实战使用jdk+tomcat+mysql进行项目部署
信息:
操作系统:CentOS-7-x86_64-DVD-1810.iso
Jdk : 1.8.0_271
Tomcat: 8.5.63
Mysql: 5.6.42
项目文件:openmrs.war
首先项目文件会存放在/opt目录下
把项目文件移动到Tomcat目录下的webapps
然后启动Tomcat服务,开启mysql服务
注:是远程进行安装的,那么mysql必须开启远程连接
授权之后,在进行重载授权表
之后重启服务
防火墙记得开放8080、3306端口,或者是关闭防火墙,这里使用的是关闭防火墙
然后再浏览器中输入ip+8080/项目名字
之后进入本机的情况进行选择
开始安装
安装好之后就跳转到登录页面
到此就安装成功了
成功登录
单元测试(Java)
语句覆盖
判断覆盖法
条件覆盖法
判定条件覆盖法
组合覆盖
路径覆盖
基本路径覆盖法
Java项目关联junit
右键项目à Build PathàAdd External…
之后将这三个添加进入
这样就可以了
创建一个java文件
Junit核心
测试用例的结构
测试用例(TestCase): 创建和执行测试用例
断言(Assert)
测试结果(TestRunner)
测试运行器(Runner)
Junit基础注解 (声明测试用例)
@Test
该注解用于标记测试方法。被标记的方法将作为测试用例进行执行。可以将该注解应用于公共的、无返回值的、无参数的方法。
代码演示:
package junit_test01.junit01;
import static org.junit.Assert.*;
import org.junit.Test;
import junit.framework.Assert;
public class DemoTest01 {
@Test
public void test02() {
Assert.assertEquals(2, 2);
}
}
在上面示例中,创建一个DemoTest01类,test02() 方法使用 @Test 注解标记,表示它是一个测试方法。在该方法中,我们执行一个加法操作并使用 assertEquals() 方法断言结果是否符合预期。
@Before
该注解用于标记在每个测试方法之前执行的方法。被标记的方法将在每个测试方法执行之前被调用,用于准备测试环境。
代码演示
package junit_test01.junit01;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import junit.framework.Assert;
public class demoTest02 {
@Before
public void setUp() {
System.out.println("开始执行测试用例");
}
@Test
public void test01() {
System.out.println("test01");
Assert.assertEquals(10, 10);
}
@Test
public void test02() {
System.out.println("test02");
Assert.assertEquals(20, 20);
}
}
运行结果
在上面的示例中,我们创建了一个名为 demoTest02的测试类.。
在测试类中,我们使用 @Before 注解标记了一个名为 setUp() 的方法。该方法会在每个测试方法执行之前被调用,打印出开始执行测试用例。
@After
该注解用于标记在每个测试方法之后执行的方法。被标记的方法将在每个测试方法执行之后被调用,用于清理测试环境。
代码演示
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import junit.framework.Assert;
public class demoTest02 {
@Before
public void setUp() {
System.out.println("开始执行测试用例");
}
@After
public void tearDown() {
System.out.println("用例执行完成");
}
@Test
public void test01() {
System.out.println("test01");
Assert.assertEquals(10, 10);
}
@Test
public void test02() {
System.out.println("test02");
Assert.assertEquals(20, 20);
}
}
在上面的示例中,我们创建了一个名为 demoTest02的测试类.。
在测试类中,我们使用 @Before 注解标记了一个名为 setUp() 的方法。该方法会在每个测试方法执行之前被调用和@After注解标记了一个名为tearDown()的方法,改方法会在每个测试方法执行之后被调用。如下图
@BeforeClass
该注解用于标记在测试类加载时执行的方法。被标记的方法在测试类加载时只执行一次,通常用于初始化静态资源。
代码演示
package junit_test01.junit01;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import junit.framework.Assert;
public class demoTest02 {
@BeforeClass
public static void setUpClass() {
System.out.println("开始执行一次");
}
@Before
public void setUp() {
System.out.println("每个测试用例开始运行执行一次");
}
@After
public void tearDown() {
System.out.println("每个测试用例结束运行执行一次");
}
@Test
public void test01() {
System.out.println("test01");
Assert.assertEquals(10, 10);
}
@Test
public void test02() {
System.out.println("test02");
Assert.assertEquals(20, 20);
}
}
在上面的示例中,我们创建了一个名为 demoTest02的测试类.。
在测试类中,我们使用 @BeforeClass了一个名为 setUpClass方法。该方法会在所有测试方法执行之前被调用,并且只执行一次
注意:@BeforeClass 方法必须是 public static void,没有参数,并且不返回任何值。
运行结果
@AfterClass
该注解用于标记在测试类卸载时执行的方法。被标记的方法在测试类卸载时只执行一次,通常用于释放静态资源。
代码演示
package junit_test01.junit01;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import junit.framework.Assert;
public class demoTest02 {
@BeforeClass
public static void setUpClass() {
System.out.println("开始执行一次");
}
@AfterClass
public static void tearDownClass() {
System.out.println("结束执行一次");
}
@Before
public void setUp() {
System.out.println("每个测试用例开始运行执行一次");
}
@After
public void tearDown() {
System.out.println("每个测试用例结束运行执行一次");
}
@Test
public void test01() {
System.out.println("test01");
Assert.assertEquals(10, 10);
}
@Test
public void test02() {
System.out.println("test02");
Assert.assertEquals(20, 20);
}
}
在上面的示例中,我们创建了一个名为 demoTest02的测试类.。
在测试类中,我们使用 @AfterClass了一个名为 tearDownClass方法。该方法会在所有测试方法执行之后被调用,并且只执行一次
注意:@AfterClass 方法必须是 public static void,没有参数,并且不返回任何值。
运行结果
@Ignore
该注解用于标记要忽略的测试方法。被标记的方法将被跳过执行,可以用于临时禁用某个测试方法。
代码演示
package junit_test01.junit01;
import static org.junit.Assert.*;
import org.junit.Ignore;
import org.junit.Test;
public class demoTest03 {
@Test
@Ignore("跳过")
public void test01() {
System.out.println("test01");
}
@Test
public void test02() {
System.out.println("test02");
}
}
在这个代码中test01中使用了@Ignore注解,所有在测试用例执行的时候,便跳过了test01
运行结果
@RunWith
用于指定自定义的测试运行器。通过该注解,可以使用自定义的运行器扩展JUnit的功能。
Junit断言方法
断言方法位于 org.junit.Assert类中
assertEquals(expected, actual)
验证两个值是否相等。可以用于比较基本数据类型、对象、数组等。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Test;
public class demoTest01 {
@Test
public void test01() {
int expected = 4;
int actual = 2 + 2;
assertEquals(expected, actual);
}
}
在 test01() 方法中,我们声明了两个变量 expected 和 actual,分别表示预期结果和实际结果。
使用 assertEquals(expected, actual) 断言方法来验证预期结果和实际结果是否相等。如果预期结果和实际结果相等,测试将继续执行。如果它们不相等,测试将失败,并抛出一个断言失败的异常
assertSame(expected, actual)
验证两个对象是否为同一对象,即引用是否相同。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Test;
import main.java.Demo01;
public class demoTest01 {
@Test
public void test01() {
Demo01 demo01 = new Demo01("子恒", 18);
Demo01 demo02 = demo01;
assertSame(demo01, demo02);
}
@Test
public void test02() {
Demo01 demo01 = new Demo01("子恒", 18);
Demo01 demo02 = new Demo01("子恒Pro", 19);
assertSame(demo01, demo02);
}
}
在 test01() 方法中,我们创建了一个名为 demo01 的 Demo01 对象,并将其赋值给另一个变量 demo02。因为对象是引用类型,所以 demo01 和 demo02 引用的是同一个对象。因此,assertSame(demo01, demo02) 断言会通过。
在 test02() 方法中,我们创建了两个不同的 Demo01 对象。demo01 被实例化为 "子恒" 和 18,而 demo02 被实例化为 "子恒Pro" 和 19。这两个对象的内容不同,因此它们引用的是不同的对象。因此,assertSame(demo01, demo02) 断言会失败。
assertTrue(condition)
验证条件是否为真。如果条件为真,断言通过;否则,断言失败。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Test;
import main.java.Demo01;
public class demoTest01 {
@Test
public void test04() {
int number = 10;
assertTrue(number > 0);
}
@Test
public void test05() {
int number = 10;
assertTrue(number < 0);
}
}
在 test04() 方法中,我们声明了一个整数变量 number,并将其赋值为 10。然后,我们使用 assertTrue(number > 0) 来验证 number 是否大于 0。由于 number 的值为 10,它满足条件 number > 0,所以断言通过,测试通过。
在 test05() 方法中,我们同样声明了一个整数变量 number,并将其赋值为 10。然后,我们使用 assertTrue(number < 0) 来验证 number 是否小于 0。由于 number 的值为 10,它不满足条件 number < 0,所以断言失败,测试失败。
assertTrue(condition) 方法用于验证给定的条件是否为真。如果条件为真,断言通过。如果条件为假,断言失败,并抛出一个断言失败的异常。
assertFalse(condition)
验证条件是否为假。如果条件为假,断言通过;否则,断言失败。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Ignore;
import org.junit.Test;
import main.java.Demo01;
public class demoTest01 {
@Test
public void test01() {
int number = 10;
assertFalse(number < 0);
}
@Test
public void test02() {
int number = 10;
assertFalse(number > 0);
}
}
在 test01() 方法中,我们声明了一个整数变量 number,并将其赋值为 10。然后,我们使用 assertFalse(number < 0) 来验证 number 是否不小于 0。由于 number 的值为 10,它不满足条件 number < 0,所以断言通过,测试通过。
在 test02() 方法中,我们同样声明了一个整数变量 number,并将其赋值为 10。然后,我们使用 assertFalse(number > 0) 来验证 number 是否不大于 0。由于 number 的值为 10,它不满足条件 number > 0,所以断言通过,测试通过。
assertFalse(condition) 方法用于验证给定的条件是否为假。如果条件为假,断言通过。如果条件为真,断言失败,并抛出一个断言失败的异常。
assertNull(object)
验证对象是否为null。如果对象为null,断言通过;否则,断言失败。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Test;
import main.java.Demo01;
public class demoTest02 {
@Test
public void test() {
Object object = null;
assertNull(object);
}
}
在 test() 方法中,我们声明了一个对象变量 obj,并将其赋值为 null,表示对象引用为 null。
使用 assertNull(object) 断言方法来验证对象是否为 null。在这个例子中,我们验证了 obj 是否为 null。如果对象为 null,断言通过。如果对象不为 null,断言失败,并抛出一个断言失败的异常。
assertNotNull(object)
验证对象是否不为null。如果对象不为null,断言通过;否则,断言失败。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Test;
import main.java.Demo01;
public class demoTest02 {
@Test
public void test01() {
Demo01 demo01 = new Demo01();
assertNotNull(demo01);
}
}
在 test01() 方法中,我们创建了一个名为 demo01 的 Demo01 对象实例。
使用 assertNotNull(demo01) 断言方法来验证对象是否不为 null。在这个例子中,我们验证了 demo01 是否不为 null。如果对象不为 null,断言通过。如果对象为 null,断言失败,并抛出一个断言失败的异常。
由于在 test01() 方法中创建了 demo01 对象,并将其赋值给变量 demo01,因此 demo01 不为 null,所以断言会通过。
assertArrayEquals(expectedArray, actualArray)
验证两个数组是否相等。用于比较数组中的元素是否相同。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Test;
import main.java.Demo01;
public class demoTest02 {
@Test
public void test02() {
int[] arr01 = new int[] {1,2,3,4};
int[] arr02 = arr01;
assertArrayEquals(arr01, arr02);
}
@Test
public void test03() {
int[] arr01 = new int[] {1,2,3};
int[] arr02 = new int[] {1,2,5};
assertArrayEquals(arr01, arr02);
}
}
在 test02() 方法中,我们创建了一个整数数组 arr01,其元素为 {1, 2, 3, 4}。然后,我们将 arr01 的引用赋给另一个数组变量 arr02。由于数组是引用类型,arr01 和 arr02 引用的是同一个数组对象。因此,assertArrayEquals(arr01, arr02) 断言会通过。
在 test03() 方法中,我们创建了两个不同的整数数组。arr01 的元素为 {1, 2, 3},而 arr02 的元素为 {1, 2, 5}。这两个数组的内容不同,因此它们引用的是不同的数组对象。因此,assertArrayEquals(arr01, arr02) 断言会失败。
assertThrows(expectedExceptionType, executable)###
验证代码是否抛出了预期的异常。可以用于检查代码是否正确处理异常情况。
fail() ###
直接使测试失败,用于指示测试未能达到预期结果。
Junit参数化/数据驱动 ###
Junit测试套件
@RunWith(Suite.class) 与@SuiteClasses
使用@RunWith注解标记测试类,并指定Suite.class作为运行器。该注解用于创建一个测试套件,将多个测试类组合在一起。
在使用@RunWith(Suite.class)注解的测试类中,使用@SuiteClasses注解来指定包含的测试类。可以将多个测试类作为参数传递给@SuiteClasses注解。
代码演示
package test.java;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
demoTest01.class,
demoTest02.class
})
public class demoTest03 {
// 这个类不需要包含任何代码
}
我们有一个名为 demoTest03 的测试类。通过使用 @RunWith(Suite.class) 注解,我们告诉 JUnit 使用 Suite 类作为测试运行器来运行这个测试类。
@Suite.SuiteClasses 注解用于指定包含在测试套件中的测试类。在示例中,我们指定了两个测试类 demoTest01.class 和 demoTest02.class。
总结起来,@RunWith(Suite.class) 注解用于创建测试套件,@Suite.SuiteClasses 注解用于指定包含在测试套件中的测试类。通过使用测试套件,可以一次性运行多个相关的测试类,以提供整体的测试报告和结果。
@RunWith(Parameterized.class)###
Suite接口 ###
除了使用注解的方式,还可以通过实现Suite接口来创建测试套件。测试类需要实现该接口,并实现suite()方法,该方法返回一个包含要组合的测试类的TestSuite对象。
Junit测试优先级顺序
在JUnit中,默认情况下,测试方法的执行顺序是不确定的。这是因为JUnit鼓励测试方法之间的独立性,不依赖于彼此的执行顺序。测试方法应该是相互独立的,可以在任何顺序下独立执行,以确保测试结果的可靠性。
然而,有时候需要指定特定的测试方法执行顺序,例如,某些测试方法依赖于其他测试方法的执行结果。在JUnit 4及之后的版本中,可以使用以下方式指定测试方法的执行顺序:
@FixMethodOrder
执行顺序使用数字,是按照数字的顺序执行的
注解:使用该注解标记在测试类上,可以指定测试方法的执行顺序。该注解有一个参数 MethodSorters,可以选择不同的排序方式,如:
MethodSorters.DEFAULT:默认方式,不保证执行顺序。
MethodSorters.NAME_ASCENDING:按照方法名的字母顺序升序执行。
MethodSorters.JVM:根据JVM返回的方法顺序执行。
按照方法名的字母顺序升序执行。
package test.java;
import static org.junit.Assert.*;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import main.java.Demo01;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class demoTest02 {
@Test
public void testC() {
int[] arr01 = new int[] {1,2,3};
int[] arr02 = new int[] {1,2,5};
assertArrayEquals(arr01, arr02);
}
@Test
public void testB() {
int[] arr01 = new int[] {1,2,3,4};
int[] arr02 = arr01;
assertArrayEquals(arr01, arr02);
}
@Test
public void testA() {
Demo01 demo01 = new Demo01();
assertNotNull(demo01);
}
}
执行结果
Junit异常 ###
Junit内置方法
Assert 类(具体使用看junit断言方法)
org.junit.Assert 类是JUnit中的核心类之一,提供了一组断言方法,用于验证预期结果和实际结果是否一致。常用的断言方法包括:
assertEquals(expected, actual):验证两个值是否相等。
assertSame(expected, actual):验证两个对象是否为同一对象。
assertTrue(condition):验证条件是否为真。
assertFalse(condition):验证条件是否为假。
assertNull(object):验证对象是否为null。
assertNotNull(object):验证对象是否不为null等等。
Assume 类
org.junit.Assume 类提供了一组用于前置条件检查的方法,用于在测试方法执行之前进行条件判断。如果前置条件不满足,那么测试方法将被忽略,不会执行。常用的方法包括:
注:被忽略的用例任然会显示成功的标准,但是没有执行
- assumeTrue(condition)
如果条件为真,继续执行测试;否则,忽略测试。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Assume;
import org.junit.Test;
public class demoTest04 {
@Test
public void test() {
Assume.assumeTrue(condition());
System.out.print(123);
}
private boolean condition() {
// 返回一个条件结果
return true;
}
}
这里返回的是true,那么就不会跳过该用例,则会在控制台中输出123,反之则会跳过该用例。
- assumeFalse(condition)
如果条件为假,继续执行测试;否则,忽略测试。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Assume;
import org.junit.Test;
public class demoTest04 {
@Test
public void test() {
Assume.assumeFalse(condition());
System.out.print(123);
}
private boolean condition() {
// 返回一个条件结果
return false;
}
}
在这里与上面的刚好相反,如果返回的是false,那么则会继续执行这个用例,如果为true,则会跳过用例
- assumeNotNull(object)
如果对象不为null,继续执行测试;否则,忽略测试。
代码演示
package test.java;
import static org.junit.Assert.*;
import org.junit.Assume;
import org.junit.Test;
public class demoTest04 {
@Test
public void test() {
Assume.assumeNotNull(condition());
System.out.print(123);
}
private boolean condition() {
// 返回一个条件结果
return false;
}
}
在这个代码中,只要返回的值不是null,就会执行这个用例,如果是null则跳过
- assumeNoException(exception)
只有当没有抛出exception异常时才会执行测试方法,否则跳过。
Parameterized 类(参数化)###
org.junit.runners.Parameterized 类用于支持参数化测试。通过该类,可以在不同的输入值下重复运行相同的测试方法。使用 @RunWith(Parameterized.class) 注解标记测试类,并使用 @Parameters 注解来提供测试数据源。
TemporaryFolder 类 ###
org.junit.rules.TemporaryFolder 类是一个测试规则,用于在测试过程中创建临时文件夹。它提供了一些方法来创建、获取和删除临时文件和文件夹,以便进行测试。
Timeout 类 ###
org.junit.rules.Timeout 类是一个测试规则,用于设置测试方法的超时时间。可以在测试类中使用 @Rule 注解来声明 Timeout 规则,并指定超时时间。
Junit编码规范
Junit 编码规范的建议:
- 使用有意义的测试方法名:测试方法名应该清晰、准确地描述被测试的功能或场景。命名应该使用驼峰命名法,并遵循一致的命名约定。
- 每个测试方法独立:每个测试方法应该独立运行,不应该依赖于其他测试方法的执行结果。确保每个测试方法都有自己的测试数据和预期结果。
- 使用@Before和@After进行准备和清理:使用@Before注解标记在每个测试方法之前执行的方法,用于准备测试环境。使用@After注解标记在每个测试方法之后执行的方法,用于清理测试环境。这样可以确保每个测试方法都在相同的环境下执行。
- 使用@BeforeClass和@AfterClass进行静态准备和清理:使用@BeforeClass注解标记在测试类加载时执行的方法,用于进行静态资源的初始化。使用@AfterClass注解标记在测试类卸载时执行的方法,用于释放静态资源。这些方法应该是静态的。
- 使用合适的断言:使用合适的断言方法来验证预期结果和实际结果是否一致。选择合适的断言方法可以使测试代码更具可读性和表达力。
- 编写清晰的测试代码:测试代码应该具有清晰的结构和可读性。使用适当的缩进和代码布局,添加必要的注释来解释测试的意图和预期结果。
- 使用@Test注解标记测试方法:使用@Test注解明确标记测试方法,以确保它们被正确地识别为测试用例。
- 使用@Ignore注解进行临时禁用:使用@Ignore注解标记暂时不需要执行的测试方法,可以在需要重新启用时轻松地找到并启用它们。
- 使用测试规则和扩展:JUnit提供了一些测试规则和扩展,可以进一步扩展和定制测试的行为。根据需要,可以使用适当的规则和扩展来增强测试的功能和可读性。
- 维护良好的测试覆盖率:尽量编写足够的测试用例,以覆盖被测试代码的不同分支和边界情况。这有助于提高代码的可靠性和稳定性。
测试运行和结果分析
- 学习如何运行单元测试,执行测试用例并收集结果。
- 掌握如何分析测试结果,识别测试通过或失败的情况。
测试覆盖率
- 了解测试覆盖率的概念和重要性。
- 学习如何使用工具计算代码的测试覆盖率,如JaCoCo等。
模拟和桩测试
- 学习如何使用模拟框架(如Mockito)创建模拟对象和桩对象,以便在测试中模拟外部依赖。
- 掌握如何配置和使用模拟和桩对象进行测试。
参数化测试
- 学习如何使用参数化测试,以在不同输入数据下运行相同的测试用例。
- 掌握参数化测试的注解和配置方法。
测试套件和测试组织
- 了解如何使用测试套件和测试组织来组织和管理多个测试用例。
- 学习如何创建测试套件和测试组织的注解和配置。
持续集成和自动化测试
- 了解持续集成的概念和流程,以及如何将单元测试集成到持续集成环境中。
- 学习如何自动化执行和监控单元测试。
最佳实践和高级技术
- 学习一些单元测试的最佳实践,如测试命名规范、测试代码的可维护性等。
- 了解一些高级的单元测试技术,如参数化测试工厂、测试的依赖注入等。
测试文档
功能测试
性能测试
性能测试理论
为什么要做性能测试?
- 业务需求:
大量业务需求,系统能否稳定运行
如:
电商双11活动/微信春晚几亿人同时抢红包/12306春运订票
当前服务配置是否支持200000人同时使用
技术选型,如编程语言选择java?Python?PHP?
性能测试通常意义上都是说的服务器的性能
前端:
App/web
APP性能:内存、CPU、电量、流量、流畅度
后端:
服务器性能
性能测试概述
什么是性能?
性能:就是软件质量属性中的“效率”特性
效率特性:
- 时间特性:指系统处理用户请求的响应时间(卡/不卡)
- 资源特性:指系统在运行过程中,系统资源的消耗情况
CPU
内存
磁盘(磁盘的写入input和读取Output,简称IO)
什么是性能测试?
概念定义:使用自动化工具,模拟不同场景,对软件各项性能指标进行测试和评估的过程就是性能测试。
- 后台处理程序性能(代码性能)
- 中间件、数据库、架构设计等是否存在瓶颈
- 服务器资源的消耗(CPU、内存、磁盘、网络)
性能测试的目的
- 评估当前系统能力
例如:验收第三方提供软件
例如:获取关键性能指标,与其它类似产品进行比较(例如手机、电脑,不同类型跑分测试)
- 发现性能问题之后,寻找性能瓶颈(模拟真实场景)、优化性能(例如:12306春运时性能故障)
- 评估软件是否能够满足未来需要(例如淘宝2023年双十一销售额)
性能测试与功能测试
焦点不一样
- 功能测试:验证软件系统操作功能是否符合产品功能需求规格,主要焦点在功能(正向、逆向)
- 性能测试:验证软件系统是否满足业务需求场景,主要焦点是业务场景的满足(时间、资源)
关系
- 功能测试和性能测试是相辅相成的,对于一款优秀的软件产品来讲,他们是不可减少的2个重要环节;
- 注意:一般新项目中,先功能测试通过后,再进行性能测试。
性能测试策略
性能测试策略,就是模拟不同的场景,这里的策略就是总结出来的一些模拟用户使用的场景。
- 基准测试
狭义上讲:也是单用户测试,测试环境确定之后,对业务模型中的重要业务单独的测试,获取用户运行时的各项性能指标(进行基础的数据采集)。
广义上讲:是一种测量和评估软件性能指标的活动。你可以在某个时刻通过基准测试建立一个已知的性能水平(称为基准线),当系统的软硬件环境发生变化之后再进行一次基准测试以确定那些变化对性能的影响。如图
基准测试数据的用途:
- 为多用户并发测试和综合场景测试等性能分析提供参考依据
- 识别系统环境的配置变更对性能响应带来的影响
- 未系统优化前后的性能提升/下降提升提供参考
- 负载测试
说明:通过逐步增加系统负载,测试系统性能的变化,并最终确定在满足系统的性能指标情况下,系统所能够承受的最大负载量的测试。
例如举哑铃
30斤以内都在承受范围,40斤的时候就有一定的压力,之后慢慢增高。所以在30~40斤之间是最合适的,而60斤是最大负载
通过负载测试,一般能找到系统的最优负载和最大负载
最大负载一般是项目内部知道的,不会对外公布
普通用户看到的系统的最大能力,一般都是测试得到的最优负载。
而在电梯中的最大承受,这里对应的是最优负载,一遍情况是不能使用最大,内部知道就可以了,普通人能看到的一般都是最优
负载:指向服务器发送的请求数量,请求越多,负载越高。
注意:负载测试关注的重点是逐步增加压力。
如图:
(1)在 (A-B) 范围内,增加系统的在线用户数,系统的处理能力 (负载量)会增强(最小负载范围内)
(2) 在 (B-C) 范围内,增加系统的在线用户数,系统的处理能力 (负载量)不变(负载压力基本饱和)
(3)在(C-D)范围内,增加系的在线用户数,系统的处理能力 (负载量)会减小(超过负载极限)
(4)系统能处理的最大用户数量为 (C)
(5)系统长时间稳定运行时的推荐用户数量为-(B)
(6)需求要求:系统在正常使用 (长时间使用) 时的用户量为 (B-C) 区间(接近C)
- 稳定测试
说明:稳定测试是指:在服务器稳定运行(用户正常的业务负载下)的情况下进行时间测试,并最终保证服务器能满足线上业务需求,时长一般为1天,一周等。
- 其它:并发测试、压力测试、容量测试
并发测试:并发测试是指在极短的时间内,发送多个请求,来验证服务器对并发的处理能力,如:抢红包、抢票
压力测试:压力测试是在强负载(大量数据、大量并发用户等)下的测试,查看应用系统在峰值的使用情况操作行为,从而有效地发现系统的某项功能隐患、系统是否具有良好的容错能力和可恢复能力,压力测试分为高负载下的长时间(24小时以上)的稳定性压力下的各个极限负载情况下导致系统崩溃的破坏性压力测试。(一般对应上图中的C~D点之间)
容量测试:关注软件的极限压力下的各个极限参数值,例如:最大TPS, 最大连接数,最大并发数,最多数据条数等。
性能测试指标介绍
什么是指标?
说明:一些经过运算得出的结果,来衡量某种操作性的统称,比如错误率0.5%
指标:在性能测试的过程中,记录的一系列的数据值,用这些实际记录的数据值与需求中的性能要求做对比,达成需求要求则无问题,未达到需求则说明是性能bug。
性能指标
- 响应时间
说明:响应时间指用户从客户端发起一个请求开始,到客户端接受到从服务器端返回的结果,整个过程所消耗的时间就是响应时间。
组成: 响应时间= 网络时间+应用程序处理时间
- 并发数
说明:并发测试的用户数
如图
系统用户数:系统注册的总用户数据
在线用户数:某段时间内访问系统的用户数,这些用户并不一定同时向系统提交请求
并发用户数:某一物理时刻同时向系统提交请求的用户数
- 吞吐量
说明:吞吐量(Throughput)指的是单位时间内处理的客户端请求数量;直接提现软件系统的性能承载能力
- 从业务角度来看,吞吐量也可以用“业务数/小时”、“业务数/天”、“访问人数/天”、“页面访问量/天”来衡量
2. 从网络角度来看,还可以用“字节数/小时”、“字节数/天”等来衡量网络的流量
3. 从技术指标来看,可以用每秒事务数 (TPS) 和每秒查询数(QPS)来衡量服务器具体性能处理能力
TPS:
说明:Transactions Per Second 每秒事务数(界面上的操作)(单位时间内系统处理的客户端请求的事务次数)
一个事物通常指的是界面上的一个操作,一个事物可以包含一个或者多个接口请求
对于登录事务而言,当TPS为10时,服务器的QPS也是10,
对于支付事物而言,当TPS为10时,服务器的QPS是30.
QPS:
说明:QPS(Query Per Second)每秒查询数
应用:控制服务器每秒处理指定请求数(如:控制服务器达到每秒60QPS,服务器的性能各项性能指标是否正常)(衡量web服务器处理能力的一个重要指标)
- 点击数
说明:点击数是衡量web服务器处理能力的一个重要指标。
点击数通常一般人认为的访问一个页面就是1次点击数,点击数是该页面包含的元素(图片、链接、JS等等)加载时,向服务器请求的数量。
通常我们也用每秒点击次数(Hits per Second)指标来衡量web服务器的处理能力。
注意:只有web项目才有次指标
- 错误率
说明:系统在负载情况下,失败业务的概率。
错误率不是功能的错误或者bug
指的是在高负载的情况下,业务失败的次数/业务的总次数*100%
- 资源利用率
指的是系统各种资源的使用情况,一般“资源的使用量/总的资源可用量*100%”形成资源利用率的数据
CPU不高于80%(正负5)à在电脑里的所有程序(操作系统、软件程序、磁盘拷贝)都是CPU完成
内存不高于80% à 程序运行时,所消耗的空间(存储程序运行的数据)
磁盘不高于90% à 存储本地的数据文件,与内存产生交互
网络不高于80% à 影响数据在网络中的传输速度
- PV和UV
性能测试流程
- 性能测试需求分析
(1)熟悉被测系统
- 熟悉被测系统的业务功能
- 熟悉被测系统的数据架构
(2)明确性能测试内容
- 从业务角度明确测试内容
确定关键业务,即:用户使用频率较高的业务能力
- 从技术角度明确测试内容
如:通常逻辑复杂较高的业务也是CPU密集运算较大的地方,考量服务器CPU在预定性能 指标下是否达标
如:通常数据量较大的业务很占用系统内存,考量服务器内存在预定性能指标下是否达标
(3)明确性能策略
负载测试
稳定测试
并发测试
(4)明确性能测试的指标
- 无明确需求指标
通过查找相关资料,和类似的系统对比,以及对未来流量的预估,确定性能测试需求的指标
- 有明确需求指标
例如:类型一下指标
下订单业务并发20个用户
平均响应时间要小于等于3s
事务成功率为100%
CPU使用率小于等于85%
只需要根据限制性分析结果与预期指标做对比,如果不满足,就需要分析问题所在
- 性能测试计划及方案
说明:性能测试实施第一份文档,也是最重要的一份文档
主要内容
- 项目背景 à 简介
- 测试目的
- 测试范围 à 对于需求分析中的性能测试内容
- 测试策略 à 对应于需求分析中的测试策略
- 风险控制 à 技术风险,人力风险
- 交付清单 à 每个阶段的产出物
- 进度与分工 à 谁在什么时候做什么事
- 性能测试用例
要素:用例标题、用例编号、用例预置条件、用例步骤、用例预期解雇、用例实际结果(需要检测的各项实际指标)
- 测试脚本编写/录制
说明:性能测试咏柳编写完成以后,接下来就需要结合用例的需要,进行测试脚本的编写工作。
提示:录制或编写,根据不同的工具要注意代码冗余。
- 建立测试环境
说明:在进行性能测试之前,需要手完成性能测试环境的搭建工作,测试环境一般包括硬件环境、软件环境及网络环境
提示:一般情况下可以要求运维和开发工程师协助完成。
- 执行测试脚本
说明:先保证脚本调试通过之后,才能进入正式测试阶段
执行测试脚本时,要先进行性能运行场景的设置吗,,在运行脚本
- 性能测试监控
性能监控就是监控服务器的各项性能指标,例如:监控CPU,内存,网络,TPS 磁盘IO等
- 性能分析与调优
说明:性能测试分析人员,经过对结果的分析以后,有可能提出系统存在性能瓶颈
提示:
调优人员(开发人员、数据库管理员、系统管理员、网络管理员、性能测试分析师)相关人员对系统进行调整。
之后进行验证,性能测试人员继续进行第二轮、第三轮……的测试,与以前的测试结果进行对比,从而确定经过调整以后系统的性能是否有提升。
- 性能测试报告总结
性能测试总结要包含以下内容:
1.性能测试需求覆盖情况,测试过程回顾,及测试中出现的问题(如何去分析、调优、解决的) ---基本要求。
2.性能测试过程中遇到各类风险是如何控制的: 目前是否还有其他的性能风险存在。
3.经过该项目性能测试后,有那些经验和教训等内容。
性能测试工具
常用的性能测试工具LoadRunner与JMeter
主流性能测试工具
LoadRunner
HP LoadRunner是一种工业级标准性能测试负载工具,可以模拟上万(百万)户实施测试,并在测试时可实时检测应用服务器及服务器硬件各种数据,来确认和查找存在的瓶颈!
支持多协议: Web(HTTP/HTML)、Windows Sockets、FTP、ODBC、MS SQL Server等协议。
最初是Mercury公司采用C语言编写,现被HP公司收购
- 优点
- 多用户(支持数量单位万)
- 详细报表分析
- 支持IP欺骗
- 缺点
- 收费
- 体积庞大(单位GB)
- 无法定制功能
JMeter
JMeter是Apache组织开发的基于java的开源软件,用于对系统做功能测试和性能测试。
它最初被设计用于web应用测试,但后来扩展到其它测试领域,例如静态文件、java程序、shell脚本、数据库、ftp、mail等
- 优点
- 免费
- 开源
- 小巧(最新版本50M左右)
- 丰富学习资料及扩展组件
- 应用广泛
- 易上手
- 缺点
- 不支持IP欺骗
- 分析和报表能力相对于较弱、不够精准
Jmeter环境搭建
首先安装jdk
Jmeter的安装
(144条消息) 测试工具JMeter详细安装配置教程(保证一次安装成功)_jmeter安装_Bella_7的博客-CSDN博客
Jmeter的启动方式
- 到bin目录下找到jmeter.bat 运行
- 双击ApacheJMeter.jdk 选择使用java程序打开
- 在终端中输入jmeter打开
注意:启动的终端窗口不能关闭,如果关闭了程序也会关闭
JMeter功能概要
JMeter文件目录介绍
- bin目录
存放着可执行文件和配置文件
Jmeter.bat:Windows的启动文件
Jmeter.log : 日志文件
Jmeter.sh:Linux的启动文件
Jmeter.properties:系统配置文件 (后续需要经常改动)
Jmeter-server.bat:Windows分布式测试要用到的服务器配置(多台机器)
Jmeter-server: Linux分布式测试要用到的服务器配置
- docs目录
docs是jmeter的api文档,可打开api/index.html页面来查看
打开可以看到jmeter所有的api接口,主要用于二次开发
- printable_docs目录
printables_docs的usermanual子目录下的内容是jmeter的用户手册文档
usermanual下的component_reference.html是最常用到的核心元件帮助文档
提示:printable_docs的子目录下有一些常用的jmeter脚本案例,可以作为参考
有任何不会都可以到这里查看,所有使用方法都写到里面了
- bil目录
改目录用来存放jmeter依赖的jar包和用户扩展所依赖的jar包
bil下的ext存放的是第三方插件
jmeter修改语言与外观
jmeter临时改为中文
这种方式如果重启或者是关闭之后重新打开,则会继续变成英文。
永久修改(修改配置文件)
到配置文件jmeter.properties中进行修改
搜索language
默认的
修改为zh_CN
之后保存重启
之后不管是重启,还是关闭重启打开,都是中文了
外观修改
JM元件作用域和执行顺序
- 元件的基本介绍
元件:多个类似功能组件的容器(类似于类)
常见的元件有:
-
- 线程组:模拟用户。
- 取样器:用户发送请求,类似于接口自动化的中request库。
- 逻辑控制器:控制语句执行顺序的,类似于Python中的逻辑控制语句。
- 前置处理器:在请求发送之前执行,类似于自动化中setUp()部分
- 后置处理器:在请求发送之后执行,类似于自动化中tearDown()部分
- 断言:响应结果,进行断言,类似于自动化中的assert语句
- 定时器:等待一定时间,类似于Python中sleep()
- 测试片段:封装的一段代码,供脚本调用,不直接执行,类似于Python中自己封装的类。
- 配置元件:对参数数据进行赋值,也就是参数化,类似于自动化中的参数化。
- 监听器:查看脚本运行结果,类似于Python中的结果打印。
组件:实现独立的某个功能(类似于方法)
- 元件作用域
在jmeter中,元件的作用域是靠测试计划的树形结构中元件的父子关系来确定的。
提示:和兴是取样器,其它组件都是以取样器为核心运行的,组件添加的位置不同,生效的取样器也不同。
只能控制下级,平级的不能控制
如图
第一个线程组中添加了一个http请求,,这种就有父子的关系。(主要看缩进是否相同)
然后第二个线程组和第一个线程组就平行了,所以它们两的关系就是独立的
对于第二个http请求,是在if控制器下面,所以按照缩进来看第二个http请求就属于if控制器的请求
这个就是作用域的关系
注:如果在后续的时候时,设置了变量没有生效,那么就要看看作用域是否符合。
作用域的原则:
- 取样器:元件不和其它元件相互作用,因此不存在作用域的问题;
- 逻辑控制器:元件只对其子节点中的取样器和逻辑控制器作用(只能控制下级,平级不能控制);不作用于其它元件
- 其它六大元件:除取样器和逻辑控制元件外,如果是某个取样器的子节点,则该元件对其父子节点起作用。
- 如果父节点不会取样器,则其它作用域是该元件父节点下的其它所有后代节点(包括子节点,子节点的子节点等等);也就是 如果父节点是取样器的话,则只对父节点起作用,如果父节点不是取样器,则对父节点下的所有组件起作用
案例:
这里我们添加了一个定时器,等待两秒钟去请求百度,
这时,我们在http请求平级在添加一个http请求,访问搜狐,
可以看到还是两秒,说明第二个http请求并没有受到定时器的作用。
Jmeter使用实例
Jmeter第一个案例
需求:使用jmeter访问百度首页接口,并查看请求和响应信息
操作步骤
- 启动jmeter
- 在“测试计划”下添加“线程组”
- 在“线程组”下添加“http请求”取样器
- 填写“http请求”的相关请求数据
- 在“线程组”下添加“查看结果数”监听器
- 点击“启动“按钮运行,并查看结果
点击启动后会出现保存
Jmeter线程组的特点和分类
- 线程组的特点
模拟多人操作
点击线程组就可以看到一下配置参数
如果是要模拟多个人的话,我们就将线程数更改,线程数是多少,那么就是多少个人
Ramp-Up时间
Ramp-Up指的是启动时间,也就是我们模拟的这些用户需要多久启动起来。
这里默认是1秒钟,如果是发送10个请求,一秒钟就发送完成了
如果将时间改为5秒
这里使用的是4秒,存在一点点误差
所以我们通过线程组里面的Ramp-up属性,可以设置用户上升的时间,也就是加载用户的时间。
为什么需要设置这个:
在做性能测试的时候,要模拟的是一万个或者是十万个用户,在正常使用这个系统的时候,就算你这个系统存在十万个用户,但是这个些用户是同时使用你这个系统的吗,一般是不会,是慢慢的进来;
设置这个Ramp-up目的是为了模拟性能测试的场景,跟接近用户的使用习惯(用户慢慢接入系统)
循环次数
这个就类似于Python中的for循环,指定循环次数,这个就是循环发送5次请求。
但是这个循环次数有一个永远的选项,如果选择,就是无限次的循环,不会停止,除非自己点击停止
这个永远不会单独使用的,是配合着下面的选项使用
延迟创建直到需要
这个的作用就是在创建线程数10,并设置Ramp-up延时5秒,如果是不选中延迟创建知道需要,那么会直接就占用10线程数的资源,但是选择这个之后,会慢慢的根据需要进行创建。直接使用不到。
持续时间
如何配合:
如果选择了永远,一般会配合着下面的调度器,并且设置一下持续的时间(运行多久)持续时间是和永久循环配合着使用的。
启动延迟
这里还有一个启动延迟的作用是就是等待n秒钟,例如Python中的sleep方法,强制等待,用的情况比较少
练习:
使用1个线程组,添加http请求
配置线程数为2,循环数为3时,运行观察结果
配置线程数为3,循环数为2时,观察解雇,对比不同
唯一相同:发出的用户请求数是一样的
但是从性能的维度上讲,区别是比较大的,
如果同时配置,实际发送的http请求数应该为m*n
虽然发送请求的次数相同,但是不能相互替换
线程数:代表并发用户数,体现服务器的负载量(压力)。
循环次数:代表执行时间。
也就是说你的线程数设置的很低,但是循环次数设置的很长很长,虽然发送的请求数很多很多,但是系统是没有任何的负载压力的,所以这两个值不能进行相互替换的。
线程组可以添加多个,多个线程组可以并行或者串联
如上图,这个就是属于并行(并行的顺序是不确定的),如何串行呢
如上图在测试计划中选中独立运行每个线程组,那么就是安装脚本顺序执行,并行的顺序是无法确定的
串行运行结果如下:
取样器(请求)和逻辑控制器必须依赖线程组才能使用
线程组下可以添加其它元件下组件
- 线程组的分类
普通线程组
普通的、常用的线程组、可以看做一个虚拟用户组、线程组中的每一个线程都可以理解为一个虚拟用户。
Setup线程组
一种特殊类型的线程组,可用于执行预测操作
Teardown线程组
一种特殊类型的线程组,可用于执行测试后的工作
演示:
这里加入了setUp线程组和tearDown线程组,setUp线程组最先执行,tearDown线程组最后执行
http请求参数的设置
- 协议
这里协议可以填两个一个HTTP一个是HTTPS
- 服务器名称或IP
这里填写域名或者是IP,不能填写路径
- 端口号
- 方法
- 路径
接口请求URL中的IP+port之后的都写在这里,路径前面的不能填写到这里(get请求参数也可以写到这里)
- 内容编码
一般情况下,填写UTF-8
- 参数
这是我们放在请求ULR中的参数(写在路径里面可以,或者是这里也可以)
- 消息体数据
放在消息体中的参数
案例1
发送请求时:
协议未填写,则默认为http协议
端口未填写,则默认为80端口
将get请求参数放置到路径中填写
案例2
发送请求时
协议选择HTTPS
端口号:443
将get请求的参数放在下面的参数列表中填写,效果一样(参数比较多,建议放在下面)
案例三 post请求
方法选择post
将参数内容放到消息体数据中,在发送时参数会添加到消息体重发送
查看结果数的内容
取样器结果
显示的是统计信息,作用不太大
请求
响应
Jmeter参数化
Jmeter参数化常用方式4种
用户定义的变量
添加方式:测试计划à 线程组à 配置元件à 用户定义的变量
场景:
请求:百度一下,你就知道
要求:使用用户定义的变量配置被测系统的协议、域名、端口号
操作步骤:
- 添加线程组
- 添加用户定义变量
这里参数设置使用的是键值对的方式
在http请求中如何引用呢:使用${定义的变量名}
- 添加http请求
- 添加查看结果数
用户定义变量处理上面的这种方式之外,还有一种方式,就是在测试计划中添加
和上面的配置没有什么区别,效果是一样的
假如说现在是要做一个登录的操作,登录的脚本是同一个,如果想用不同的用户登录,要怎么做呢,使用用户定义变量这种方式就不合适了,希望要每个用户获取到这个脚本的值都不同,那么就使用用户参数的方式。
用户参数
使用用户定义变量时,不同的用户在访问时,读取的参数值完全相同,,如果希望每个用户在访问时的变量不同,可以使用用户参数。
添加方式:测试计划à 线程组à 前置处理器à 用户参数
场景:
请求:百度一下,你就知道
要求:
第一次附带参数:name=“张三”&age=”28”;
第二次请求附带参数:name=“李四”&age=”30”;
操作步骤:
- 添加线程组
- 添加用户参数
将张三李四添加上去
- 添加http请求
这里的引用方式和上面的是一样的,都是使用${变量名}
- 添加查看结果数
注:有多少个用户,就用多少个线程数,如果有4个线程数,但是只有两个用户,那么就会循环执行两次,就是每个用户执行两个。
练习:
使用用户参数,模拟不同用户登录
CSV Data Set Config数据文件设置
添加方式:测试计划à 线程组à 配置元件à CSV数据文件设置
场景:
请求:百度一下,你就知道
要求:循环3次,每次请求时附带参数username、password、code的值不相同
操作步骤:
- 定义csv数据文件,
打开时,不要使用excel,使用notepad打开
- 添加线程组
- 添加csv数据文件设置
- 添加http请求
- 添加查看结果数
函数(__counter)
计数函数:一般做执行次数统计使用
位置:在菜单中选择à 工具à 函数助手对话框
通过counter函数在生成动态变化的数值
在http取样器中,引用counter函数生成的函数字符串,就可以读取counter函数生成的数值
如果counter参数设置为TRUE,则每个用户分别从1开始计数,没循环一次,加一
如果counter参数设置为FALSE, 则所有用户公用一个计数器,没发送一个请求时,加一
Jmeter响应中出现乱码如何解决
解决方法,打开配置文件
找到这个
改为UTF-8
然后重启
之后在查看就不存在了
Jmeter断言使用
断言的概念:
断言:让程序判断预期结果和实际结果是否一致
提示:jmeter断言是在请求的返回层面增加一层判断机制,因为请求成功了,并不代表结果一定正确,因此需要检测机制提高测试准确性。
Jmeter中的常用断言
响应断言
响应断言中的常用参数:
响应文本:响应体中的数据
响应代码:响应的状态码
响应信息:响应状态码对应的信息
响应头:
请求头:
ULR样本:就是ULR
文档(文本):响应数据的文本格式
忽略状态:主要用于校验错误,那么就开启这个,不主动判断效验失败。
请求数据:请求参数
匹配规则部分,选择效验的方式(通过正则表达式的方式效验)
通过自动化的手段对请求的断言数据进行自动效验。
添加方式:测试计划à 线程组à(右键添加)断言à 响应断言
案例
请求:https://www.baidu.com
检查:让程序检查响应数据中是否包含“百度一下,你就知道”
步骤:
- 添加线程组
- 添加http请求
- 添加响应断言
我们断言是对http响应内容进行断言,http的响应数据是在http请求返回的,断言是断言的响应数据,所以我们要将断言加在http请求下面。
- 添加断言结果
- 添加查看结果数
然后执行
但是和我们正常请求也是一样的,那么如果我们在断言中输入错误的数据
注:可以在一个http请求下包含多个断言
JSON断言
该组件用来对JSON文档进行验证,验证步骤如下:
- 首先解析JSON数据,如果数据不是JSON,则失败(如果明确是JSON,那么就使用JSON的方式断言,这样效率会比较高)
- 使用jayway JsonPath1.2.0中的语法搜索指定的路径、如果找不到路径,就会失败
- 如果在文档中找到JSON路径,并要求对期望值进行验证,那么他将执行验证操作
添加方式:测试计划à 线程组 à http请求à 右键添加断言à JSON断言
案例:
场景:
请求:昆明市, 云南省天气预报和情况 - The Weather Channel | Weather.com
检查:让程序检查响应JSON数据中,city对应的内容是否是“昆明市”
操作步骤:
- 添加线程组
- 添加http请求
- 添加JSON断言
- 添加断言结果
字段说明:
Assert JSON Path exists:需要断言的 JSON 表达式
Additionally assert value:如果要根据值去断言,请勾选
Match as regular expression:如果要根据正则表达式去断言,请勾选
Expected Value:期望值
Expect null:如果期望是 null 则勾选
Invert assertion:取反
- 查看结果数
JSONPath语法元素
持续时间断言(Duration Assertion)
持续时间就是响应时间,也就是对我们的响应时间进行断言。
对客户端发送请求,到收到服务器的响应时间,要求不超过执行时间。
添加方式:测试计划à 线程组à http请求à 右键添加断言à 断言持续时间
案例:
场景:京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!
检查:让程序检测响应时间是否大于500毫秒。
步骤:
- 添加线程组
- 天剑http请求
- 添加断言持续时间
- 添加断言结果
- 添加查看结果数
如果超过200那么就会报错。
实际时间是统计的取样器结果中的load time
Jmeter关联使用
什么是关联
当请求之间有依赖关系,比如一个请求的入参(请求的参数),需要使用到之前请求的响应数据时,需要使用关联
所有提供关联功能的元件都在后置处理器中:
- 正则表达式提取器
- Xpath提取器
- JSON提取器
正则表达式提取器
这里所有的数据都可以通过一些特定的规则,进行匹配的,这个规则就是正则表达式,通过正则表达式,就可以在所有文本内容中,提取出我们想要的数据。
<title>百度一下,你就知道</title>
<title>.*?</title>
. :是通配符,可以代表任意字符(处了换行回车)
* : 代表前面的字符出现0次或者多次
?: 代表贪婪匹配,找到左边边界后,往右边查看匹配右边界,只要有匹配的右边就停止继续查找右边界。
参数介绍:
引用名称:报错提取出的数据的变量,用于后续的请求来引用。
正则表达式:从响应结果中提取出需要的数据内容,注意:加上括号才能提取出来。
模版:当正则表达式中有多个括号时,也就是多组值,你需要取第几个括号中值来保存。如图
匹配数字(0代表随机):当一个变量(一个括号)中有多个值,取第几个;1表示取第一个,-1表示取所有。
缺省值:当没有提取出数据时,返回一个默认值
添加方式:测试计划à 线程组à http请求à 右键添加后置处理器à 正则表达式提取器
场景
请求:http://192.168.1.7/niushop/niushop/ 获取网页的title
请求:百度一下,你就知道 把获取到的title作为请求参数
操作步骤:
- 添加线程组
- 添加http请求- Niushop开源商城
- 添加正则表达式提取器
- 添加http请求-百度
- 添加查看结果树
注意如果存在多个结果的话,不知道要怎么进行调用,那么就可以添加一个调试取样器
这里面会显示出所有获取到的名称,然后根据你需要的选择,填入就可以了,这里的下标是从1开始计数,根据具体的需求,选择合适的下标。
Xpath提取器
引用场景:只能适用于返回响应消息为HTML格式情况。
参数:
添加方式:测试计划à 线程组à http请求à 右键后置处理器à xpath提取器
案例:
场景
请求:http://192.168.1.7/niushop/niushop/ 获取网页的title
请求:百度一下,你就知道 把获取到的title作为请求参数
步骤:
- 添加线程组
- 添加http请求- Niushop开源商城
- 添加xpath提取器
勾选use tidy
填写引用名称:参数名
Xpath路径
- 添加http请求—百度
- 添加查看结果树
JSON提取器
JSON提取器跟xpath提取器是一样的,当我们返回的数据是JSON格式的时候,就可以使用JSON提取器来提取;作用和前面的是一样,只是提取的数据格式不一样而已
应用场景:适用于返回的数据类型为JSON格式的情况。
参数介绍:
步骤:
- 添加线程组
- 添加http请求—天气
- 添加JSON提取器
参数名
JSON路径
- 添加http请求—百度
引用JSON提取器中定义的参数名
- 添加查看结果树
上面的所有案例都是两个请求在同一个线程组下,也就是给同一个用户操作。那么就可以通过这几个提取器的方式进行操作。
但是如果说这两个请求是在不同的线程组下,如何操作?
跨线程组关联
跨线程组关联指的是多个请求之间有关联关系(即一个请求的参数需要使用前面请求的响应数据),但是两个请求不在同一个线程组内,此时使用提取器无法完成关联,需要使用jmeter属性来完成数据的传递。
当有依赖关系的两个请求(一个请求是另一个请求的返回的数据),放入到不同的线程中时,就不能使用提取器保存的变量来传递参数值,而是要使用jmeter属性来传递。
Jmeter属性的配置方法:
函数实现:
__setProperty函数:将值保存成jmeter属性
__property函数:在其他线程组中使用property函数读取属性。
类似于全局变量如图:
案例:
场景
线程组1:获取网页的标题
返回结果中的标题
线程组2:请求百度一下,你就知道,把获取到的标题作为请求的参数
操作步骤:
- 添加线程组1
- 添加http请求—标题
- 添加xpath提取器
- 添加BeanShell取样器(将xpath提取器中的值保存未jmeter属性)
然后再函数中生成
属性名称title就是jmeter属性值。
然后将值复制到BeanShell中,如下图
之后将其拷贝
- 添加线程2
- 添加http请求—百度(读取jmeter属性)
然后将生成的值复制到这里
- 添加查看结果树
这样就成功实现跨线程组关联。
Jmeter自动录制脚本
应用场景:
在没有接口文档的旧项目当中,快速录制web页面产生的http接口请求,帮助编写接口测试脚本。
代理服务器原理介绍:
代理服务器的原理主要是拦截和转发请求与响应数据。
操作步骤:
- 在jmeter当中添加非测试元件http代理服务,并进行配置
在jmeter当中添加http代理服务:测试计划(右键)à 非测试元件à http代理服务器。
- 代理设置
在Windows中打开网络和Internetà 代理设置à 手动设置代理
端口要和jmeter保持一致
- 然后就可以点击启动,进入浏览器完成相关操作,脚本会自动生成并存放在指定的线程组下。
但是在脚本里面有很多不是我们想要的内容,这里设置的是电脑的代理,那么所有的网络的请求都会被抓下来,就可以使用过滤的方法进行筛选。
包含模式:只有包含里面内容的才会抓下来。
排除模式:有的数据不抓取。
注意:这里的包含模式和排除模式都只能使用正则表达式进行填写。
这里我们设置只包含192.168.1.7里面的数据
.*192.168.1.7.*
像是一些.png .js .css 这些文件都是有cookie的,我们只需要跑一次就不用跑了,所以我们在录脚本的时候是可以不同录制这些的
配置完成之后不是立即生效的,我们要点击重启
之后在进行录制,发现少了很多
Jmeter cookie提取
在上面脚本的基础上现在录制登录的脚本
在添加一个线程组,并且在http代理中更改目标控制器。
然后执行登录操作,之后在查看结果树种进行回放。
这里是输错错误的密码形成的
这里code:2表示登录成功了
在这个脚本的录制中是没有存在什么问题的,也能成功登录,只是脚本没有完善,缺少了cookie提取
加上之后,什么也不用修改,直接再一次回放cookie
这个是登录成功的
Cookie管理器:自动将cookie信息添加到后续的所有请求中。
登录及后续的相关操作时,需要提前添加http cookie管理器中。
自动记录请求中的登录状态
当录制登录及后续相关操作的所有操作时,在录制的脚本运行时,需要添加http cookie管理器,脚本才能正确运行。
Jmeter 其它操作
Jmeter直连数据库###
逻辑控制器
逻辑控制器可以按照设定的逻辑控制取样器的执行顺序
- 常用的逻辑控制器
If 控制器
循环控制器
ForEach控制器
- If控制器
If控制器用来控制它下面的测试元素是否运行
添加方式:ceshi计划à 线程组à 右键添加逻辑控制器à if 控制器
案例:
需求:
使用用户自定义的变量 定义一个变量name,name的值可以是“baidu”或者是“192.168.1.10”
根据name的变量值实现对应网站的访问
操作步骤:
- 添加线程组
- 用户定义的变量
- 添加if控制器,判断name是否等于baidu
- 添加http请求,来访问百度
- 添加if控制器,判断name是否等于192.168.1.10/niushop/niushop/
- 添加http请求,来访问niushop
- 添加查看结果数。
根据上面的设置,我们在用户定义的变量里面就写了“baidu”,那么在执行结果的时候,只会访问百度
上图红色的并不是报错,而是建议:
为了实现性能,建议检查“将条件解释为变量表达式”
和use_jexl3或_groovy计算为真或假,或一个包含真或假的变量。
如果使用use_jex13 或者是_groovy函数,那么就要勾选interpret condition as variable expression
如果我们勾上了,但是还是使用JSON的方式来写,那么就不对了,必须使用函数的方式来写
之后运行的结果还是一样的
- 循环控制器
通过设置循环次数,来实现循环发送请求
添加方式: 测试计划à 线程组à 右键添加逻辑控制器à 循环控制器
案例1:用户定义的变量
需求:循环访问百度10次
操作步骤:
- 添加线程组
- 添加循环控制器
这里只需要设置一下循环次数即可。
- 添加http请求
- 添加查看结果树
注意: 这里的循环控制器和线程组里面的循环次数的区别是两者的作用域不相同,线 程组的循环是控制整个线程组的,而循环控制器只针对控制器下面的请求
- ForEach控制器
ForEach控制器一般和用户自定义变量或者正则表达式提取器一起使用,其在用户自定义变量或者从正则表达式提取器的结果中读取一系列相关变量。该控制器下的取样器都会被执行一次或者多次,每次读取不同的变量值。
与用户定义的变量或者正则表达式提取器配合使用,循环读取用户定义的变量或者正则表达式结果中的所有数据。
添加方式: 测试计划à 线程组à 右键添加逻辑控制器à ForEach控制器
参数解析
案例:
需求:
有一组关键字[hello, python, 测试],使用用户定义的变量存储
要依次取出关键字,并在百度中搜索。例如:百度安全验证
操作步骤:
- 添加线程组
- 用户定义的变量
参数名:固定前缀+连续的数字后缀
- 添加ForEach控制器
- 添加http请求
在ForEach控制器下方添加http请求,并引用ForEach读取的数据${word}
- 添加查看结果树
案例2:正则表达式演示
####
定时器
同步定时器(Synchronizing Timer)集合点
提示:在jmeter中叫做同步定时器,在其它软件中又叫做集合点。
思考:
如何模拟多个用户同时抢一个红包?
如何测试电商网站中的秒杀?
原理:
SyncTimer的目的是阻塞线程,直到阻塞了n个线程,然后立即释放他们。
同步定时器相当于一个储蓄池,累积一定的请求,当在规定的时间内达到一定的线程数量,这些线程会在同一个时间点一起并发(),所以可以用来做大数据量的并发请求。
添加方式:测试计划à 线程组à http请求à 右键添加定时器à Synchronizing Timer
案例:
场景:
模拟100个用户同时访问百度首页,统计高并发请求下运行情况
操作步骤:
- 添加线程组,设置线程数=100
- 添加http请求
- 添加同步定时器
- 添加查看结果树
- 添加监听器-聚合报告
注意:
练习:
模拟100个用户同时访问百度首页,统计高并发请求下运行情况
配置一次运行20个用户,观察结果
配置一次运行30个用户,观察结果
常数吞吐量定时器(Constant Throughput Timer)
介绍:
常数吞吐量定时器可以让jmeter以指定数字的吞吐量(以每分钟的样本数为单位,而不是每秒)执行。吞吐量计算范围可以为指定当前线程、当前线程组、所有线程组
添加方式:测试计划à 线程组à http请求à 右键添加定时器à Constant Throughput Timer
案例:
场景
一个用户以5QPS(5次/S)的频率访问百度首页,持续一段时间,统计运行情况。
操作步骤:
- 添加线程组,循环次数设置成永远
- 添加http请求
- 添加常数吞吐定时器
- 添加查看结果树
- 添加监听器-聚合报告
注意:常数吞吐量定时器只是帮忙达到性能测试的负载(压力)要求,本身不代表有bug/无bug,对于bug的分析,需要通过响应时间来判断。
jmeter分布式
在使用jmeter进行性能测试时,如果并发数比较大(比如项目需要支持10000并发),单台电脑的(CPU和内存)可能无法支持,这时可以使用jmeter提供的分布式测试的功能。
Jmeter分布式原理:
Jmeter分布式测试时,选择其中一台作为控制机(controller),其它机器作为代理机(Agent)。
执行时,控制机会把脚本发送到每台代理机上,代理机拿到脚本后就开始执行,代理机执行时不需要启动jmeter界面,可以理解它是通过命令行模式执行的。
执行完成之后,代理机会把结果回传给控制机,控制机会收集所有代理机的信息汇总。
注意事项
- 系统上的防火墙已关闭或打开了正确的端口(杀毒软件也是)。
- 所有客户端都位于同一子网中。
- 如果使用 192.x.x.x 或 10.x.x.x IP 地址,则服务器位于同一子网中。 如果服务器不使用 192.xx 或 10.xx IP 地址,则应该没有任何问题。
- 确保JMeter可以访问服务器。
- 确保在所有系统上使用相同的JMeter和Java版本。混合版本将无法正常工作。
- 要关闭jmeter中的RMI SSL开关
分布式演示:
代理机配置:
- Agent机上需要安装jmeter
- 修改服务端口
- 将RMI SSL设置为禁用
这里false是开启,true是关闭
- 运行Agent上的jmeter-server.bat文件启动jmeter
控制机Controller配置
- 修改jmeter bin文件下的jmeter.properties配置文件,修改remote_hosts
- 将RMI SSL设置为禁用
- 启动jmeter
配置好之后,运行,这两个都可以,不可以使用哪个绿的小箭头运行
运行之后代理机的日志
Jmeter测试报告
聚合报告
位置:测试计划à 右键à 监听器à 聚合报告
如上图
其中,中位数表示一半用户的响应时间(毫秒)
90%百分位表示的就是90%
重要的性能指标:
- 响应时间
观察当前最大最小值的波动范围,如果波动范围不大,那么就以平均响应时间作为最终的性能响应时间结果。
如果波动范围很大,那么就以90%的响应时间,来作为最终的性能响应时间结果。
- 错误率
- 吞吐量
上面的是通过一个组件来查看对应的结果,但是对于jmeter而言,也提供了一个最终的报告来给我们,HTML的方式,能看到一些测试过程中详细的信息,
生成HTML测试报告
命令:
参数描述:
。-n: 非GUI模式执行JMeter
。-t [imx file]: 测试计划保存的路径及jmx文件名,路径可以是相对路径也可以是绝对路径
。-l[result file]: 保存生成测试结果的文件,jtl文件格式
。-e: 测试结束后,生成测试报告
。-o [htmlreport folder]: 存放生成测试报告的路径,路径可以是相对路径也可以是绝对路径注意:result;jt和report会自动生成,如果在执行命令时result.jtl和repot已存在,必须用先删除,否则在运行命令时就会报错
演示:
命令:jmeter -n -t "D:\软件\apache-jmeter-5.5\bin\常数吞吐量定时器.jmx" -l resule.jtl -e -o ./report
End of run 表示执行结束了
然后就可以查看文件
Jmeter并发数计算
pv(page view)即页面访问量,每打开一次页面pv计数+1,刷新页面也是。pv只统计页面访问次数。
UV(Unique Visitor) 唯一访问用户,用来衡量真实访问网站的用户数量。
一般uv统计用户活跃数量,用pv统计用户访问页面的频率。
普通计算公式:
计算公式:TPS = 总请求量/总时间
如上图,在2019年第32周,有4.13万的浏览量,那么总请求,我们可以认为估算为4.13万次(一次浏览对应一个请求)
总请求= 4.13万请求= 41300请求。
总时间= 1天 = 1 * 24小时 = 24 * 3600秒
TPS= 41300 / 24 * 3600 = 0.48请求数/秒
结论:按照普通计算方法,我们在测试环境对相同的系统进行性能测试时,每秒能够发送0.48请求就可以满足线上需求。
但是这种方式计算被平均了,一个系统的访问不可能永远的处于一个线性、水平的访问,一定会有时候高,有时候低,这种方式不是很准确。
二八原则计算方式
二八原则就是80%的请求在20%的时间内完成。
计算公式= TPS = 总请求数(80%) / (总时间20%)
上面的需求安装这个公式来计算那么就是:
TPS = 41300 * 0.8请求数 / 24 * 3600*0.2秒= 1.91请求数/秒
结论:按照二八原则计算,在测试环境我们的TPS只要能达到1.91 请求数每秒就能满足线上需要。二八原则的估算结果会比平均值准确率更高
按照业务数据进行计算
- 计算模拟用户正常业务操作(稳定测试)的并发量
业务数据:有的公司会统计一定时间内的所有业务数据,我们可以根据这个业务数据直方图,统计最多请求数量和时间比例。
根据这个图可以看出主要的请求时间都是在8~24点,在1~7点的时候是不是没有什么人在使用这个系统,如果在计算TPS的时候将1~7点的时间计算进去,是不是存在不准确。
根据上图可以得出结论:
大部分订单在8~24点之间,因此系统的有效工作时长为16个小时
从订单数量统计:8~24点之间的订单站一天总订单的98%左右(40474个)
结合二八原则的计算公式:
TPS = 总请求80% / (总时间20%)
需要在测试环境模拟用户正常业务操作(稳定性能测试)的并发数量为:
TPS = 40474 * 0.8的请求数 / 16*3600*0.2 = 2.81请求数/秒
按照这种业务数据统计的方式+二八原则的方式,相对于比较准确的,用于稳定测试没有问题的,但是不排除一些极端情况,这里的二八原则计算的还是属于一个均值
极端情况:类似于电商的双十一秒杀
- 计算模拟用户峰值业务操作(压力测试)的并发量:
根据这些数据统计图,可以得出结论:
订单最高峰在21~22点之间,一小时的订单总数大约为8853个
计算压力测试的并发数:TPS = 峰值请求数 / 峰值时间 * 系数
系数:表示,你未来的性能是当前2倍,那么就*2, 3倍就*3(淘宝的双十一就是这么来计算的),所以系数可以是:2、3、6、10,由项目组自己觉得要达成的性能指标
TPS = 8853 / 3600秒 * 3(系数) = 7.38请求数/秒
练习题:
某购物商城,进过运营统计,正常一天的交易值为100亿,客单价平均为300元,交易时间主要为17:00~24:00,其中19:00~20:00的成交额最大,大约20亿。
现升级系统,需要进行性能测试,保证软件在上线后能稳定运行。
请计算出系统稳定测试时的并发(负载)量,及保证系统峰值时的并发(负载)量
基于需求:有效的交易时间为:10:00~14:00,17:00~24:00,一共为7个小时
有效的请求数= 100e / 300
稳定的TPS = 有效的请求数 * 0.8 / 7 * 3600 * 0.2
100e / 300 / 11*3600*0.2
= 3367请求数/秒
压力测试:
峰值的交易时间19:00~20:00,一共一个小时
峰值有效的请求数= 20e / 300
峰值的TPS = (20e / 300) / 3600 * 系数
假设这里的系数为3
TPS = (20e/300)/ 3600 * 3 = 5555.5
Jmeter常用图表###
服务器资源指标监控###
项目实战
目标:
- 熟悉项目的功能模块和技术架构
- 掌握如何进行性能测试点的提取
- 掌握性能测试计划包含的主要内容
- 掌握如何编写性能测试用例
- 熟练掌握如何编写jmeter测试脚本
- 知道如何建立性能测试环境
- 掌握如何执行测试脚本
- 掌握性能测试监控关键指标
- 知道如何进行性能测试瓶颈分析
- 知道如何进行性性能调优
- 掌握性能测试报告包含的主要内容
轻商城项目介绍
背景
轻商城项目是一个现在流行的电商项目,我们需要综合评估改项目中各个接口的性能,并给出优化建议,满足日常需求。
简介
轻商城是一个支持web和微信小程序的前后端分离架构的项目
前端使用VUE技术框架开发,即支持微信小程序,也支持手机移动端,还支持web页面
后端使用了SpringBoot框架进行开发,MySQL做数据库
项目功能架构
小商城功能
- 首页
- 专题列表、专题详情
- 分类列表、分类详情
- 品牌列表、品牌详情
- 新品首发、人气推荐
- 优惠卷列表、优惠卷选择
- 团购
- 搜索
- 商品详情、商品评价、商品分享
- 购物车
- 下单
- 订单列表、订单详情、订单售后
- 地址、收藏、足迹、意见反馈
- 客服
管理平台功能
- 会员管理
- 商城管理
- 商品管理
- 推广管理
- 系统管理
- 配置管理
- 统计报表
项目技术架构
前端(看得到的部分—HTML,css,JS技术代码实现):
微信小程序
Web页面
后端(看不到的部分—通过后端技术代码实现:java、c、Python)
服务器(应用服务器、数据库服务器、后台业务逻辑代码)
前后端分离:指的是前端系统和后端系统是分离成两个子系统进行开发的
对外的表现是:前端发送http请求后,响应的内容为一个JSON字符串(不是HTML页面)
如果是前端不分离的项目,前端发送http请求后,响应的内容为一个HTML页面
前后端分离的项目与不分离的项目相比:
运行效率高(分离的传输的仅仅是一个JSON格式,但是如果是不分离的那么就会传输整个HTML页面)
扩展性好
数据库设计
项目搭建
项目原地址
litemall: 又一个小商城。 litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端 (gitee.com)
操作系统:Ubuntu
Mysql
Java环境
Maven
(144条消息) centos7下安装Maven_centos7安装maven_simon-_-的博客-CSDN博客
Nodejs
Centos 7 安装 Node.js v16.13.1 - 简书 (jianshu.com)
首先将网站项目的数据文件导入数据库中
注意:无需创建数据库,登录数据库之后就可以直接导入
Mysql> source /usr/local/litemall/litemall-db/sql/litemall_schema.sql
Mysql> source /usr/local/litemall/litemall-db/sql/ litemall_table.sql
Mysql> source /usr/local/litemall/litemall-db/sql/ litemall_data.sql
然后启动管理后端的后端服务
cd litemall #进入到根目录
mvn install
mvn clean package
java -Dfile.encoding=UTF-8 -jar litemall-all/target/litemall-all-0.1.0-exec.jar
启动管理后台前端
打开命令行,输入以下命令
cd litemall/litemall-admin 进入到这个目录
npm install --registry=https://registry.npm.taobao.org
npm run dev
启动轻商城前端
打开命令行,输入以下命令
cd litemall/litemall-vue
npm install --registry=https://registry.npm.taobao.org
npm run dev
然后访问链接就可以了
性能测试需求分析
性能测试需求分析与传统的功能测试需求有所不同
功能测试需求分析:重点在于分析被测系统的功能是否满足产品功能需求规格(正向、逆向)
性能测试需求分析:重点在于分析被测系统是否能满足特定的业务需求场景(时间、资源)。
需要从业务场景、程序代码、服务器、硬件配置等多个维度分析系统可能存在性能瓶颈。
功能测试:关注用户的业务功能(正向逆向)
性能测试:关注系统对于特定业务需求场景的满足程度(时间、资源)
维度:业务功能、项目代码、服务器、硬件配置
- 如何获取有效的需求
- 客户方提出:
能够提出明确需求的一般是金融、银行、电信、医疗等企业,他们一般对系统的性能要求高,并且对性能也非常了解。
提示:需要评估性能需求的合理性
- 根据历史数据分析(运营数据)
通过分析历史运营数据收集用户信息,如:
注册用户数、日活、月活,计算用户的增长速度
每月、每周、每天的峰值业务量是多少
用户频繁使用的功能模块是哪些
- 性能测试点的提取规则
- 用户频繁使用的业务功能
- 非常关键的业务功能
- 特殊交易日或者是峰值交易的业务功能
- 核心业务发生重大调整的业务功能
- 资源占用非常高的业务功能
- 轻商城性能测试点提取
- 明确性能测试目标
轻商城作为一个新开发的项目,性能测试目标包括:
- 确定核心业务功能的TPS
- 对业务流程(多接口组合)进行压力测试
- 系统能在实际系统运行压力的情况下,稳定运行24小时
期望的TPS和最大响应时间:
性能测试计划
- 测试背景
轻商城是公司新开发的一个电商项目,为了保证项目上线后能够稳定的运行,且在后期推广中能够承受用户的增长,需要对项目进行性能测试。
-------------------------------------------------------------------------------
接口测试
接口测试基础
接口及接口测试概念
- 接口
接口:是指系统或组件之间的交互点,通过这些交互点可以实现数据的交互。
接口就是数据交互的通道,在系统或组件之间,完成数据的传递。
-
- 接口的类型
划分形式,大致分为3类
- 按照协议划分:协议不同,接口类型不同。http、tcp、UDP、IP、ftp、USB……
- 按照语言划分:java、Python、c++、PHP……
- 按照范围划分:系统之间和程序内部
- 系统之间: 多个内部系统之间交互,内部系统与外部系统之间的交互
- 程序内部:方法与方法之间,模块与模块之间的交互
- 接口测试:
接口测试:是对系统或组件之间的接口进行测试,主要是效验数据的交换、传递和控制管理过程,以及相互逻辑依赖关系。
接口测试处了做数据交互之外,还要做逻辑依赖是否正确
例如:我们复制订单的地址,在没有登录的情况下,输入刚刚复制的地址,不能直接查看订单,而是应该先登录。
-
- 接口测试原理
用户工具或者代码模拟客户端向服务器发送请求,服务器接受请求后进行相应的业务处理,并向客户端返回响应数据,检查响应数据是否符合预期
数据(预期和实际)从哪里来:
用户需求
怎么效验:
工具
代码模拟客户端
-
- 接口测试的特点
- 测试可以提前介入,提早发现bug,符合质量控制前移的理念
- 可以发现一些页面操作发现不了的问题(如支付密码,输入非数字、特殊字符)
- 接口测试低成本高效益(底层的一个bug能够引发上层8个左右bug,接口测试可以实现自动化)
- 不同于传统的单元测试,接口测试是从用户的角度对系统进行全面的检测
-
- 接口测试的实现方式
使用接口测试工具来实现(如:JMeter 、postman、fiddler、burp suite)
通过编写代码来实现(如:Python + unittest + request)
什么是自动化接口测试呢?
利用工具、代码替代人工,自动判断响应结果和预期结果是否保持一致。依赖断言
http协议
协议:就是规则,要求使用双方必须严格遵守
http协议简介
http(Hyper Text Transfer Protocol)超文本传输协议,是一个基于请求与响应模式的、应用层的协议,也是互联网上应用做广泛的一种网络协议。
-
- http协议的特点
- 支持客户端/服务端模式
- 简单快速
- 灵活
- 无连接
- 无状态
http协议主要是针对B/S(浏览器服务器模式)
tcp协议主要是针对C/S
C/S模型要求在进行数据通信的双方必须先建立连接(三次握手),然后才能通信进行数据传输,当想要断开连接,那么就要进行四次挥手
这里客户端和服务端必须有一个连接保持,在连接的状态下能发送数据,当数据发送完了,连接就要断开,并且在整个过程中,都是有各种状态的
http协议在通信的时候,不需要建立连接,只需要ip+对应的端口号
http就是无连接,那么都无连接了,那么就不会有状态了。
注意:
HTTP 协议是基于 TCP/IP 协议族的应用层协议。虽然使用了 TCP 进行连接,但是在 HTTP 的设计中,它被称为"无连接"和"无状态"是指以下两个方面:
无连接(Connectionless):每个 HTTP 请求和响应都在独立的 TCP 连接上进行传输。这意味着客户端向服务器发送请求后,不需要保持连接等待服务器的响应,它可以关闭连接或者与其他服务器建立连接。同样地,服务器在响应客户端后也会关闭连接。这种设计使得 HTTP 协议具有较低的延迟和较好的可扩展性。
无状态(Stateless):这是 HTTP 协议的一个设计原则,服务器不会保存客户端的任何状态信息,即每个请求都是相互独立的。这是由于 HTTP 的服务器处理请求的方式,服务器会根据每个请求进行处理,并将响应返回给客户端。服务器不会记住以前的请求或与客户端的任何会话信息。因此,每个请求都需要包含足够的信息来完成处理。
URL
URL: (Uniform Resource Locator)统一资源定位符,是互联网上标准资源的地址。http使用URL来建立连接和数据传输。
URL格式:
http://www.itcast.cn:8080/news/index.html?uid=123&page=1
- 协议:http、https、ftp
- 域名:本质IP地址(定位网络环境中的一台主机)
- 端口:在网络主机上定位一个应用,端口号可以省略。没有指定端口,默认协议端口80
- 资源路径:对应网页的源代码(服务器网站项目的路径)或网络中的资源
- 查询参数:传参给网页源代码
语法:key=value
URL: 数据资源的定位符:协议://域名:端口/资源路径?查询参数&查询参数
http请求
作用:
由客户端发送给服务器,规定了发送给服务器的数据的语法格式!
http请求格式:
请求行 GET / HTTP/1.1
请求头 key:value格式
………
空行
请求体
注:get请求有可能是没有请求体的
请求行语法格式:
作用:指定请求方法,请求资源
语法格式:请求方法(空格)URL(空格)协议版本
请求方法:
GET : 获取网络资源 –没有请求体
POST :提交一定的信息,得到反馈 –登录、注册
HEAD : 只获取网络资源的响应头
PUT : 更新服务器资源 –有请求体
DELETE : 删除服务器资源 –没有请求体
CONNECT TRACE : 测试
OPTIONS : 获取服务器性能信息
协议版本:
http1.1 http1.2 http1.3
现阶段主流版本:http1.1
请求头语法
作用:向服务描述当前客户端(浏览器)的基本信息
语法格式:key:value
User-Agent: 向浏览器描述浏览器的类型(防止爬虫)
Content-Type:向服务器描述请求体数据的类型
请求体格式与类型
GET DELETE请求方法没有请求体
POST PUT请求方法,有请求体
请求体的数据类型,受请求头中 content-type的值影响
http响应
作用:
由服务器回发送给客户端,
规定了发送给服务器的数据的语法格式!
整体格式:
响应行(状态行)、响应体、空行、响应体
响应包(报文)
状态行
语法:协议版本(空格)状态码(空格)状态码描述\r\n
常用协议版本:http/1.1
状态码:
针对http请求,响应的状态。
1xx:100-101 提示信息,表示请求被接收
2xx:200-206响应成功
3xx:300-307响应需要进一步操作,重定向
4xx:400-415客户端错误
5xx:500-505服务器错误
响应头
作用:向客户端描述服务器的基本信息
语法:key:value
Content-Type:向客户端描述响应体的数据类型!
响应体
http响应报文,大多数是有响应体的
响应体的数据类型受响应头中content-type的值影响
常见的类型:
JSON类型
表单 类型
图片 类型
传统风格接口
- 使用get post实现所有数据的增删改查操作
- 针对 用户的某一个操作,URL不唯一
- 状态码统一使用200
好处:
- 对开发的技术要求低,只需要get post方法即可。
- 代码实现灵活
RESTful风格的接口
定义:
一种软件架构风格,设计风格,而不是标准,只是提供了一组设计原则和约束条件
REST:即(Representational State Transfer)的缩写。词组的翻译是“表现层状态转化”。如果一个架构符合REST原则,就称为RESTful架构。
只是一种建议
这里使用GET POST PUT DELETE分别表示增删改查。
使用一个URL对应一个唯一的资源。
状态码,根据实际操作请求加以区分。
界定RESTful风格的接口:
- 请求方法使用GET POST PUT DELETE 查、增、改、删
- 数据资源的定位符(URL)是唯一的
- 在URL中不使用动词,而替换使用名词。结合请求方法,界定具体操作。
接口测试流程
- 需求分析
- 主要依赖需求文档
- 接口文档解析,结合开发提供的文档,进行分析
- 一般是由开发人员编写接口文档(api文档)
- 设计测试用例(送审)
- 执行测试
- 使用接口测试工具实现
- 工具:postman、fiddler、JMeter
- 代码:Python+unittest+request
- 通过编写代码实现
- 接口(bug)缺陷管理与跟踪
- 生成测试报告
- 接口自动化持续集成(可选)
接口文档
- 接口文档介绍
- 什么是接口文档?
接口文档:又称为api文档,一般是由开发人员编写的,用来描述系统提供接口信息的文档。大家根据这个接口文档进行开发,并需要一直维护和遵守。
-
- 为什么要写接口文档
- 能够让前端开发与后端开发人员更好的配合,提高工作效率。
- 项目迭代或者项目人员更迭时,方便后期人员查看和维护。
- 方便测试人员进行接口测试
- 结构
基本结构
接口描述
URL(协议+域名)+资源路径
请求方法
参数设置
请求体
请求体(get delete没有)
返回结果
状态码、状态描述
响应体
- 接口文档解=解析
http请求:
请求行
请求方法
URL
协议版本:默认http/1.1
请求头
Content-Type
请求体
http响应:
响应行
状态码。状态描述
响应头
响应体
Niushop登录接口演示
http请求:
请求行
请求方法:POST
URL: http://192.168.1.9/niushop/niushop/index.php?s=/login/index
协议版本:默认http/1.1
请求头
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
请求体
username=ceshi001&password=123456&vertification=
http响应:
响应行
状态码。状态描述:200 OK
响应头
无
响应体
{"code":"-2001","message":"账号或者密码错误"}
{"code":1,"url":"http:\/\/192.168.1.9\/niushop\/niushop\/index.php?s=\/member\/addresslist"}
接口测试用例设计
- 为什么写
- 防止漏测
- 管理工作进入
接口测试的测试点
功能测试:
单接口功能
手工测试中每个业务功能。在接口测试中就对应唯一一个接口,针对该接口展开测试。
业务场景功能
对应手工测试中,有业务流程,就是接口的调用先后顺序,按调用顺序展开接口测试。
性能测试
响应时长
从发送请求,到服务器回发响应,所经历的时长
吞吐量
TPS 每秒处理的请求
并发数
同一时间,同时向服务器接口发送请求,所能正确处理的数量
服务器资源使用率
接口工作中,服务器硬件资源使用占比情况
安全测试
敏感数据是否加密
如密码、省份证、银行卡号
SQL注入
在能输入的地方输入SQL语句,测试是否能查询到结果。
其它各种漏洞的验证
设计方法与思路
接口用例设置的测试点与手工页面业务功能的测试点事一样的
手工功能用例设计的要点:
- 测试页面布局、控件的位置是否准确
- 针对用户名的编辑框中的数据值,展开测试
正确的用户名、有特殊字符、8位
- 针对密码的编辑框中的数据值,展开测试
正确密码、错误密码、特殊字符、1位、100位
等等
接口用例设计要点:
- 面中的用户名的编辑的值,对应接口中key为username的value值。针对username的值展开测试
正确的用户名、有特殊字符、8位等等
- 手工页面中的用户名编辑框的值,对应接口中key为password的value值。针对password的value值展开测试。
正确密码、错误密码、特殊字符、1位、100位
等等
单接口测试用例
8大要素:编号、标题、项目描述、优先级、预置条件、测试数据、执行步骤、预期结果
接口测试用例:
编号、标题、用例名称、优先级、接口名称、测试方法、URL、请求头、请求体(请求数据),预期结果
登录测试点
数值正向
登录成功
数值反向
账号为空
账号有特殊字符
账号未注册
密码错误
密码为空
密码1位
参数正向:
必选参数(全部参数):
参数反向
多参数
少参数
无参数
错参数
注意:里面的状态码比如-2001、-3001这些都是在开发程序时自己定义的。
业务场景功能
分析测试点:
业务场景尽量遵循用户实际使用的场景,按顺序调用接口进行测试
尽量使用最少的测试用例覆盖最多的业务场景
例如这里有增加、修改、删除,我们使用一个业务场景将其覆盖即可
登录(前提条件)--添加地址—修改地址—删除地址
这个场景,一般情况下,主需要测试正向的业务场景即可!
项目环境说明
Postman实现接口测试
Postman介绍和安装
Postman是一款非常流行的接口测试工具,它使用简单,功能强大,不仅测试人员会使用,开发人员也会使用。
主要特点:
简单易用的图形用户界面
可以保存接口请求的历史记录
使用测试集Collections可以更有效的管理组织接口
可以在团队之间同步接口数据
安装:
双击文件即可。
Postman基本用法
案例一
使用postman发送http请求,访问tpshop商城的搜索接口
实现步骤:
- 使用抓包工具或者是浏览器开发者工具,获取搜索接口使用的请求方法和URL
请求方法:get
请求URL:
http://www.ziheng870.com/index.php/Home/Goods/search.html?q=%E6%89%8B%E6%9C%BA
- 使用post去创建一个标签页,,组织http请求,发送搜索接口请求
案例二
使用postman向tpshop商城登录接口发送一个密码错误的登录请求。
获取数据:
请求方法:post
URL:http://www.ziheng870.com/index.php?m=Home&c=User&a=do_login&t=0.5248974743562411
请求头:Content-Type: application/x-www-form-urlencoded; charset=UTF-8
请求体:username=15555555555&password=123&verify_code=8888
步骤:
- 首先设置请求和URL
- 设置请求头
- 设置请求体
&savedate=1
btnPost=%E7%99%BB%E5%BD%95&username=admin&password=0192023a7bbd73250516f069df18b500&savedate=1
结果
案例三,JSON格式的登录
使用postman向轻商城登录接口发送一个密码错误的登录请求。
获取数据:
请求方法:post
URL:http://192.168.1.7:6255/wx/auth/login
请求头:Content-Type: application/json
请求体:{"username":"user123","password":"user123"}
步骤:
这里选择raw的格式有多种,记得选择成JSON的。
结果:
错误的密码
正确的:
Postman高级用法
管理测试用例Collections(用例集)
导出用例集和导入
导出:
然后点击export之后,就会弹出保存的位置。
注:不建议修改文件的后缀
导入:
然后再这个界面,将文件移动到里面即可,或者是点击files也是可以的
这里由于文件已经存在了,如果导入的是一个不存在postman的文件,那么就不会出现这个问题。
Postman断言
断言介绍
断言:让程序判断预期结果和实际结果是否一致。
特点:
- Postman的断言是使用JavaScript语言编写的,写在Tests标签页里面。
- Tests中的脚本在发送请求之后执行,会吧断言的结果(PASS/FAIL)最终在“TestResults”标签页中展示。
Postman常用断言
断言响应状态码
Status code:Code is 200
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
Pm: postman的一个实例
Test(): postman实例的一个方法,有两个参数;
参数1 "Status code is 200" 断言完成之后给出的提示信息。
参数2 匿名函数调用
pm.response.to.have.status(200) 函数的意思 postman的响应结果中,有状态码200
添加
注意:如果点击send发送不了,那么可能是URL、请求方法、请求头、请求体出现错误,而不是断言。
断言响应体是否包含某个字符串
Response body: Contains string
代码
pm.test("Body matches string", function () {
pm.expect(pm.response.text()).to.include("msg");
});
pm: postman的一个实例
test(): postman实例的一个方法,有两个参数;
参数1 " Body matches string " 断言完成之后给出的提示信息。
参数2 匿名函数调用
pm.expect(pm.response.text()).to.include("string_you_want_to_search");函数的意思 postman实例预期结果中包含string_you_want_to_search字符串。
pm.response.text()是服务器返回的响应体数据
String_you_want_to_search 是你预测响应体中的具体一部分数据。
添加
断言响应体是否等于某个字符串(对象)
Response body: Is equal to a string
这个断言是用于是否完全等于某个返回响应体
代码
pm.test("Body is correct", function () {
pm.response.to.have.body("response_body_string");
});
pm.response.to.have.body("response_body_string");的意思:postman的响应结果中有响应体为response_body_string的数据
response_body_string是预期结果。
这个断言方法并不常用,了解即可。
断言JSON数据
Response body: JSON value check
代码
pm.test("Your test name", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.value).to.eql(100);
});
***********************************************************************
var jsonData = pm.response.json(); 定义一个变量,名为jsondata, 值为JSON格式的响应体数据。
jsonData.value的value指的是JSON格式中的key;
to.eql(100) 这里的100表示的就是JSON格式中的value
断言响应头
Response headers: Content-Type header check
代码
pm.test("Content-Type is present", function () {
pm.response.to.have.header("Content-Type");
});
***********************************************************************
pm.response.to.have.header("Content-Type") 意思postman响应结果中,响应头有Content-Type
注意:只要是在响应头中有的key都是可以断言的
但是这样只能断言key,我们可以在key的后面加上value,就能对值进行断言了
如图
这种有时间的断言不了的,因为时间会变
Postman断言的原理
全局变量和环境变量
全局变量设置与获取
全局变量:
全局变量是全局唯一的,不可重复的变量
手动设置
代码设置
设置:
pm.globals.set("varname", value)
演示:
var age_tmp = 18;
pm.globals.set("glb_age", age_tmp)
获取:
请求参数中获取(在postman页面中获取) :{{全局变量名}}
用于:查询参数、请求头、请求体
代码中获取:
var 接收变量名 = pm.globals.get("全局变量名")
演示:
var get_age = pm.globals.get("glb_age")
环境变量设置获取
环境变量:
- 一个变量只属于某个环境,在某一个环境中变量不可以重复定义
- 在环境与环境之间可以定义重复的变量
- 一个环境可以包含多个环境变量
- 常见环境分类:开发环境、测试环境、生成环境
手动设置
代码设置
代码:
pm.environment.set("var_name", value)
演示
var age_tmp = 19;
pm.environment.set("env_age", age_tmp)
获取:
请求参数中获取(在postman页面中获取) : {{环境变量名}}
用于:查询参数、请求头、请求体
代码中获取:
var 接收变量名 = pm.environment.get("环境变量名")
演示:
var get_age = pm.environment.get("env_age")
Postman请求前置脚本
假设,有一个场景:
调用某个接口时,要输入“时间戳”的绝对值,超过标准时间10分钟,则不允许调用。
时间戳:
表示当前系统时间。表示方式:从1970年1月1日0点~现在所经历的秒数。
请求前置脚本
书写在“Pre-request-Script”标签中
postman在http请求发送之前,会自动执行,该脚本中的代码。
案例: 获取当前时间戳,将时间戳传递给百度服务器。
查看是否被执行
查看发送的历史数据包和数据包的数据
工作原理:
练习:
需求:在tpshop项目中获取图片验证码的接口,需要在URL中设置随机数
请求路径:
http://www.ziheng870.com/index.php?m=Home&c=User&a=verify&r=0.18164136674040243
分析:在JS中如何实现生成随机数?
Postman关联
介绍:
- Postman的关联,用来解决接口和接口之间调用的依赖关系。需要借助 全局变量、环境变量来结局。
例如地址添加,是需要先登录之后才能进行添加的,那么这种就是所谓的关联关系。
实现步骤:
以A接口返回的数据 供B接口使用为例:
- 组织A接口http请求数据,发送A接口请求。
- 获取A接口 返回的响应数据。
- 组织B接口的http请求,从全局变量、环境变量中获取A返回的数据,从而实现关联操作。
案例:
请求获取手机号地址,提取响应接口中的地址,将地址信息给百度搜索接口使用。
天气接口:
https://cx.shouji.360.cn/phonearea.php?number=18888888888
百度接口:百度一下,你就知道
创建postman关联,用例集和http请求两个
先提取手机地址的返回结果中的数据,然后再Tests中编写脚本提取信息存储到全局变量中
之后在百度接口中调用全局变量,之后在点击执行
然后再查看结果
成功!
批量执行用例集
步骤:
- 点击用例集名称,点击run按钮,会进入runner标签页中。
- 在runner标签页中 用例集的所有http请求内容会被默认选中。
- 再次点击run+用例集名称 的按钮进行批量执行用例集。
具体案例:
结果
扩展:
多次执行用例集
Postman参数化
使用场景:
如果某一个接口需要不通类型的数据类型进行验证,不如登录界面每次需要不同用户名和密码的排列组合进行验证,那么就是接口一致,参数不一致时,我们可以通过外部文件进行读取参数进行测试。
Csv
示例:测试不同账号的登录都能够登录成功,并且验证是否登录成功,(账号数据从csv中读取)。
首先 先创建一个csv文件,并存储用户名、密码和返回的JSON数据中的errmsg状态。
然后设置URL 和请求头
之后在设置请求体,这里将变量作为参数放到请求中。
之后进入批量执行用例集
注意:这里你csv文件中有几条数据,那么导入之后运行的次数就会是多少次。
注意:如果在预览的时候出现了乱码,那么就要对csv文件进行设置编码成utf8。
设置断言
这里使用了两种断言一个断言响应状态码和JSON断言,JSON断言有两种方式,建议使用方式1。
之后执行
也是运行成功,并且每次登录的数据都是不同,并且返回的数据也是不一样的。
JSON
Json的方式和csv文件方式是一样的,就是文件格式不一样,
还是使用上面的案例
首先先创建json文件
注:最外面要以中括号开始,里面的数据使用的是key : value的方式。
然后设置URL 和 请求头
设置请求体
这里的username和password还是json文件中的key。
之后导入json文件
之后设置断言,和上面的还是一样,json断言中的assert是导入json文件中的assert。
之后执行:
Postman测试报告
项目实战
代码实现接口测试
Request库
集成unittest
接口测试框架开发
项目实战
持续集成
持续集成介绍
Git与git代码托管平台
Jenkins
持续集成与postman
持续集成之代码
接口测试扩展
https
webserver
接口mock测试
接口测试总结
-------------------------------------------------------------------------------
Postman工具
- 学习Postman工具的基本功能和界面。
- 熟悉如何创建和发送接口请求。
接口请求设置
- 学习如何设置接口请求的URL、请求方法、请求头等信息。
- 掌握常见的请求参数设置,如查询参数、请求体等。
参数设置和变量设置
- 学习如何设置接口请求的参数,以模拟不同的场景和数据负载。
- 掌握变量设置的概念和使用方法,以便在请求和断言中引用变量。
测试断言
- 了解断言的概念和作用,以验证接口的响应和结果。
- 学习如何设置和使用断言,包括响应状态码、响应体等。
数据驱动和数据文件
- 掌握数据驱动的概念和方法,以使用不同的测试数据执行接口测试。
- 学习如何使用数据文件(如CSV、JSON等)作为测试数据源。
添加cookie
- 学习如何添加和管理Cookie,以模拟用户的会话状态。
- 掌握如何在接口请求中设置和使用Cookie。
接口测试执行
- 学习如何执行接口测试,包括单个接口的测试和测试集合的执行。
- 掌握测试执行的相关选项和配置。
测试报告和结果分析
- 学习如何生成接口测试报告,包括测试结果的总结和统计数据。
- 掌握如何分析测试结果,识别接口问题和性能瓶颈。
实践项目
- 参与实际的接口测试项目或模拟场景,应用所学知识进行实践。
- 设计和执行不同类型的接口测试,模拟不同的接口调用和数据负载。