【高效编码】深入TestNG体验了一把,真爽

简介: 您好,我是码农飞哥,感谢您阅读本文!如果此文对您有所帮助,请毫不犹豫的一键三连吧。小伙伴们,有啥想看的,想问的,欢迎积极留言。上一篇文章对TestNG做了一个简单的体验。我们了解了其是什么,有什么用,重点介绍了其各种注解。但是这些都不够深入,不够爽。实际项目中怎么运用呢?没有说,实在不过瘾。

您好,我是码农飞哥,感谢您阅读本文!如果此文对您有所帮助,请毫不犹豫的一键三连吧。小伙伴们,有啥想看的,想问的,欢迎积极留言。上一篇文章对TestNG做了一个简单的体验。我们了解了其是什么,有什么用,重点介绍了其各种注解。但是这些都不够深入,不够爽。实际项目中怎么运用呢?没有说,实在不过瘾。

这篇文章就让我们来深入体验一下TestNG。看看到底好不好用,用的爽不爽。

搭建项目,添加待测试类

首先,还是搭建一个SpringBoot框架的项目,怎么搭建项目,在此就不赘述了,想了解的朋友可以参考这篇文章Spring Boot 学习01-----搭建一个简单的spring-boot-demo

项目搭建好之后,这里添加了几个待测试的方法。代码如下:

@RestController
public class TestNgController {
    @Autowired
    private TestNgService testNgService;
    @PostMapping("/v1/user/add")
    public String addUser(String userName, String password) {
        boolean result = testNgService.saveUser(userName, password);
        return result ? "{\"code\":\"200\",\"msg\":\"用户保存成功\"}"
                : "{\"code\":\"500\",\"msg\":\"用户保存失败\"}";
    }
    @GetMapping("/v1/user/get")
    public String getUser(String userName) {
        boolean result = testNgService.getUserByName(userName);
        return "{\"code\":\"200\",\"msg\":\"获取用户成功\"}";
    }
    @RequestMapping("/v1/user/update")
    public String updateUser(@RequestBody User user) {
        boolean result = testNgService.updateUser(user);
        return result ? "{\"code\":\"200\",\"msg\":\"用户修改成功\"}"
                : "{\"code\":\"500\",\"msg\":\"用户修改失败\"}";
    }
}

这里分别有:

1.用户保存接口/v1/user/add,请求类型是application/form-data。

2.获取用户接口/v1/user/get,请求类型是application/form-data。

3.用户更新接口/v1/user/update ,这个的请求类型是application/json

三个接口,下面分别对这三个接口进行单元测试。

引入依赖

针对接口的测试需要引入的依赖有,spring-boot-starter-test主要是引入Spring-Test 依赖,因为需要使用 @SpringBootTest,testng依赖就不用多说了,今日份主角。

<dependency>
          <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.10</version>
            <scope>test</scope>
        </dependency>

编写单元测试基类

2.设置单元测试基类

@SpringBootTest
public class TestNgControllerTest  extends AbstractTestNGSpringContextTests {
    private MockMvc mockMvc;
    @Autowired
    private WebApplicationContext webApplicationContext;
    @BeforeClass
    public void setUp(){
    //必须用webAppContextSetup方法设置web环境
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
    }
  //通用返回结果的封装
   public String getResultCode(MockHttpServletRequestBuilder requestBuilder) {
        try {
            MvcResult mvcResult = mockMvc.perform(requestBuilder)
                    .andDo(print())
                    .andExpect(status().isOk())
                    .andReturn();
            String result = mvcResult.getResponse().getContentAsString();
            JSONObject jsonObject = JSON.parseObject(result);
            return jsonObject.getString("code");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
  }

是不是感觉跟前面Junit 整合SpringBoot的单元测试基类有点像呢?对的没错,长的确实有点像,因为其中MvcResult对象和MockMvc对象因为来自spring-test。所以,getResultCode方法是一模一样的,都是针对返回类型是application/json格式的返回做了处理。

需要特别注意的是与Junit的不同点:

1.继承AbstractTestNGSpringContextTests 类。

因为这个类实现了ApplicationContextAware接口(Spring会检查实现该接口的类,并通过setApplicationContext方法注入ApplicationContext对象)。可以获取到这个ApplicationContext系统上下文对象,而WebApplicationContext对象是ApplicationContext对象的子类。

2.通过 @BeforeClass 注解修饰的方法来初始化MockMvc对象。

最主要的原因是AbstractTestNGSpringContextTests类中通过springTestContextPrepareTestInstance来准备初始化的对象

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextBeforeTestClass")
  protected void springTestContextPrepareTestInstance() throws Exception {  this.testContextManager.prepareTestInstance(this);
  }

所以在@BeforeClass运行之前执行的方法都不能获取对象。即使用@BeforeSuite或者是@BeforeTest注解修饰的话,则初始化方法运行时ApplicationContext还没有被注入进来。如果用@BeforeMethod修饰的话,则每个测试方法执行之前都会执行一次。

补充说明:@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextBeforeTestClass") dependsOnMethods属性表示该方法依赖于springTestContextBeforeTestClass方法,就是说springTestContextBeforeTestClass必须要先于springTestContextPrepareTestInstance执行,同时alwaysRun = true 表示依赖者和被依赖者之间必须不存在成功失败的因果关系,只需要被依赖的方法执行即可。

编写单元测试方法

基类编写好之后,就相当于是打好了基础,下面只要搬砖了。首先编写保存用户的测试方法

@Test
    public void testAddUser() {
        System.out.println("线程="+Thread.currentThread().getName()+"执行testAddUser");
        //保存成功
        {
            MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/v1/user/add")
                    .param("userName", "张三")
                    .param("password", "123");
            String resultCode = getResultCode(requestBuilder);
            Assert.assertEquals("200", resultCode);
        }
        //保存失败的情况
        {
            MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/v1/user/add")
                    .param("userName", "李四")
                    .param("password", "123");
            String resultCode = getResultCode(requestBuilder);
            Assert.assertEquals("500", resultCode);
        }
    }

这里有保存成功和保存失败两种情况。代码的编写跟使用Junit时是一模一样的,还是实例化MockHttpServletRequestBuilder对象,传入接口地址和入参。在此不在赘述了。执行结果是:

c326220c44d2cf5c58b1758bad8ba99a_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png

直接运行单元测试类,三个测试方法的运行线程都是主线程,不是说testNG可以进行并发测试么?没看到呀,不要着急,这就来进行并发测试。

并发测试

建立一个TestNG的XML文件,命名为TestNgControllerTestSuite.xml。

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="Suite" parallel="methods" thread-count="4">
    <test name="Test">
        <classes>
            <class name="com.jay.testng.TestNgControllerTest"/>
        </classes>
    </test>
</suite>

其中与并发测试相关的配置是:

<suite name="Suite" parallel="methods" thread-count="4">

在当前测试规划的执行过程中,为每个方法(不管是不是通过@Test修饰的方法)进行并发执行,最多4个线程,下面就是运行结果

f84ceab9a32c84de8edfe670c6ff31a2_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png

当然还有其他的配置,比如:

1.针对测试方法

<suite name="Suite" parallel="tests" thread-count="4">

在当前测试规划的执行过程中,为每个测试方法(只有通过@Test修饰的方法)进行并发执行,最多4个线程。

2.针对测试类

<suite name="Suite" parallel="classes" thread-count="4">

在当前测试规划的执行过程中,为每个测试类的执行使用单独的线程(该测试类中的测试方法共享一个线程),最多并发4个线程。

总结

本文详细介绍了TestNG与SpringBoot的整合,还介绍了并发测试。


相关文章
|
8月前
|
Linux 测试技术 C++
【代码实践】编码精粹:打造高效与可维护的代码艺术
【代码实践】编码精粹:打造高效与可维护的代码艺术
164 0
|
4月前
|
数据挖掘 Python
🚀告别繁琐!Python I/O管理实战,文件读写效率飙升的秘密
在日常编程中,高效的文件I/O管理对提升程序性能至关重要。Python通过内置的`open`函数及丰富的库简化了文件读写操作。本文从基本的文件读写入手,介绍了使用`with`语句自动管理文件、批量读写以减少I/O次数、调整缓冲区大小、选择合适编码格式以及利用第三方库(如pandas和numpy)等技巧,帮助你显著提升文件处理效率,让编程工作更加高效便捷。
54 0
|
3月前
|
存储 安全 Java
Map的并发处理,助你提升编程效率,代码更优雅高效。
【10月更文挑战第19天】Map使用技巧大公开:从选择合适的Map实现(如HashMap、TreeMap、LinkedHashMap)到利用Map的初始化、使用Map.Entry遍历、运用computeIfAbsent和computeIfPresent方法,再到Map的并发处理,助你提升编程效率,代码更优雅高效。
38 2
|
4月前
|
IDE Go 开发工具
自动生成代码等效率小技巧
本文介绍了GoLand中的多种便捷功能,包括动态模板、自动生成注释与测试文件、自动格式化、生成struct标签和构造参数等。动态模板可通过预定义缩写快速生成代码片段,如`imp`生成导入语句。自动生成注释插件Goanno简化了文档编写过程,支持快捷键和右键菜单调用。生成测试文件功能则通过`gotests`命令或IDE内操作实现,方便为函数和方法创建测试。此外,GoLand还支持自动格式化代码、生成struct的接口和标签等功能,提升编码效率。对于Go模块管理中的常见问题,提供了详细的解决方案,如使用`go mod init`创建`go.mod`文件来管理依赖。
37 5
|
4月前
|
SQL NoSQL Java
彻底革新你的数据库操作体验!Micronaut数据访问技巧让你瞬间爱上代码编写!
【9月更文挑战第10天】Java开发者们一直在寻找简化应用程序与数据库交互的方法。Micronaut作为一个现代框架,提供了多种工具和特性来提升数据访问效率。本文介绍如何使用Micronaut简化数据库操作,并提供具体示例代码。Micronaut支持JPA/Hibernate、SQL及NoSQL(如MongoDB),简化配置并无缝集成。通过定义带有`@Repository`注解的接口,可以实现Spring Data风格的命名查询。
83 6
|
5月前
|
自然语言处理 Java API
"告别Java8 Stream噩梦,JDFrame神器来袭!让你的代码简洁如诗,效率翻倍,编程新体验等你尝鲜!"
【8月更文挑战第11天】Java 8的Stream API以强大的函数式编程能力革新了集合数据处理方式,但其抽象概念和复杂的链式调用让不少开发者望而却步。为此,JDFrame框架应运而生,通过直观易懂的操作符简化Stream使用,减少代码量并提高效率。
138 3
|
XML 机器学习/深度学习 JSON
|
IDE Java 测试技术
【Java开发编码的工作效率问题工作经验之谈】
【Java开发编码的工作效率问题工作经验之谈】
|
SQL IDE Linux
IDEA:7个强大功能助你高效编码和优质工作!
IDEA:7个强大功能助你高效编码和优质工作!
75 0
|
XML 测试技术 应用服务中间件
【高效编码】还在用Junit么? out啦!!!快来看看这个框架。。。。
您好,我是码农飞哥,感谢您阅读本文!如果此文对您有所帮助,请毫不犹豫的一键三连吧。小伙伴们,有啥想看的,想问的,欢迎积极留言,前面几篇文章我们详细介绍了测试框架Junit的使用。但是Junit也就诸多局限性。例如:不能分组测试,不支持多线程测试,不支持参数化测试等等。针对局限,这篇文章将介绍另外一种单元测试框架TestNG。
99 0
【高效编码】还在用Junit么? out啦!!!快来看看这个框架。。。。