我不写单元测试,被批了

本文探讨了单元测试的重要性,包括单元测试的定义、类型、为何进行单元测试以及如何使用Mockito框架进行单元测试。作者分享了个人对TDD(测试驱动开发)的理解,并解释了为何有时会选择Mockito而非纯Junit。此外,还提及了集成测试的场景,并分享了自己不热衷于单元测试的原因。文章最后鼓励读者在评论区分享写单元测试的理由。

最近在看单元测试的东西,想跟大家聊聊我的感受。单元测试这块说实在的,我并不太熟悉,我几乎不写单元测试,也不太爱写单元测试。

当我推广消息推送平台austin的时候,有过批评我整个项目没有单元测试,也有过让我补上单元测试的。

01、单元测试(UNIT TEST)

我有个前同事进了外企,他说进了外企以后学习了很多新名词,刚进去时都不知道他们讲的是什么...

他问我:UT你知道是什么意思吗?

我说:不知道啊。

他说:UT是Unit Test,单元测试

我说:你们现在都要写单元测试吗?

他说:是啊

后来,我在群里闲聊的时候,发现有个企鹅上班的大哥也不知道UT是什么意思,那我就放心了。

02、测试类型有什么

测试类型对于我们开发人员来说,或许可以归纳分为三类:

1、单元测试:对某个类中的代码进行测试,查看是否正常

2、集成测试:跨模块测试查看代码是否正常

3、端到端测试:以用户的角度把系统作为一个整体看功能是否正常

所以,在我们程序员里谈单元测试的时候,可能会是纯单元测试,也可能是集成测试,毕竟这块大概率都应该是我们干的。

03、为什么要单元测试

对于我这种不怎么写单元测试的,也不爱写单元测试的,在我的嘴里自然就编不出要写单元测试的理由了,倒是不写单元测试的理由是一堆堆的。

说到单元测试,就不得不提起另一个词,TDD(Test-Driven Development)测试驱动开发:在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码

测试驱动开发虽然饱受争议,不过有这种方法论的推出并有不少的同行在践行,起码能够说明测试的重要性

1、当我们想测试部分代码逻辑是否正常的时候,我们可能会直接psvm来构造数据进而调试。那如果有一种东西能把我们psvm统一放到某个地方呢?

2、当我们在一个系统里边修改了很多代码时,又不确定改动是否影响在核心逻辑时。那如果有一种东西能在编译的时候,顺便自动跑一遍逻辑做回归呢?无论是重构还是正式提测前,都提高了自己写代码的信心。

3、当我们很容易一不小心时就把代码写成一坨屎,那如果有一种东西能让我们在编码的时候就注重自己的代码设计呢?

4、当我们这个季度什么都没干,但是系统没发生过故障,那如果有一种东西能让我们在KPI上添上浓墨的一笔呢?

5、....欢迎补充

没错,这东西就是单元测试

04、单元测试怎么写?

很长一段时间里,怎么写单元测试我的知识就停留在Junit上。后来,跟我那个进外企的小伙伴以及群里的滴滴哥交流了以后,发现他们都会用Mockito这个框架去写单元测试

在这个过程中, 我看了些关于Mockito单元测试的文章,但总会有专业术语给我劝退,到这里我就很明白,我要边写边看了。

1、SpringBoot环境下使用Mockito只需要引入spring-boot-starter-test就好了,默认内置了Mockito相关的依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
</dependency>
复制代码

2、我们单元测试是写在对应的test目录下的,想想都知道会有插件可以帮我们通过主类成为出对应的测试类,是不需要我们手动去建包和建测试类的。

于是,我找到了squaretest插件

3、看几篇Mockito相关的教程,了解其API和概念,推荐下这个教程: www.letianbiji.com/java-mockit…

4、亲自动手写一个单元测试,了解其编写过程和感受体验

有的人可能看到这里就要问了:为什么要用Mockito这种测试框架而不是纯用Junit? 在我的看来,答案就是:我们在测试时对象可能是Spring下的,我们不能直接new,又或是new出来的对象成本很大(还得解决依赖等问题)

这时候,我们就需要Mock对象出来模拟我们创建了这个对象,而在学习Mockito测试框架在这个过程中,其实就是对Mock/Stub/Spy概念的理解以及他们的使用。

MockSpy都是模拟创建出一个对象,区别在于Spy模拟创建出的对象是会真实调用方法的,而Mock模拟创建出的对象是不会真实调用方法的

Mock模拟创建出来的对象不会调用真实方法,但我们又想验证其流程怎么办?

比如,我在写service层的单元测试,我认为dao层的代码是正常的,但是service是需要dao的对象访问数据库的,这时候我Mock出dao的模拟对象,去调用方法。

所以我会假定调用dao层的某方法时它的返回值是什么,这个过程就是Stub

05、集成测试怎么写

在刚刚,我们使用Mockito的时候,是没有依赖Spring环境的,对象都是Mock出来的,速度杠杠的,非常快。但我们很多时候可能是需要依赖Spring环境跨模块去调试功能是否正常。

这时候,我们就要使用 @SpringTest来修饰测试类指明我这个是需要Spring环境的。既然有了Spring环境,那 @Autowire之类的注解都是可以用的。

在web模块下启动的话,你会发现它就真的启动了应用相关的环境,然后专门跑了这个测试方法。

05、为什么我不爱写单元测试

我是写完了业务代码,然后再回过头来写单元测试。单元测试是我自己写的,我Mock出来模拟对象再Stub,整个过程中我都是认为我写的代码是正确的。

写完了以后,看到绿色的条框我并不意外,毕竟我是对着我的业务代码写的单元测试。而集成测试都把Spring环境相关的依赖都整进去了,我直接在本地启动服务也能自己调呀。

(我相信看到这篇文章绝大多数人都不是TDD模式开发,应该都是对着自己写好的业务代码写单元测试)

写单元测试的代码也是代码,也是要花时间的呀

我也去问了腾讯/滴滴/阿里/字节的朋友,发现他们也不爱写单测,很多时候写单测就是为了通过编译,为了业务的覆盖率,能绕开就绕开了。

为什么没有问京东/拼多多/美团/网易等等的?别问,问就是还没太熟。

看到这篇文章的同行肯定是有在团队推行写单元测试的,不妨在评论区写下理由拯救一下我们这些迷途不爱写单元测试的羔羊

Java项目中使用MyBatis-Plus量生成单元测试代码,通常可以通过结合MyBatis-Plus的代码生成器(`mybatis-plus-generator`)与模板引擎(如Freemarker)来实现。该过程主要包括配置代码生成器、定义生成策略以及使用模板生成测试代码。以下是具体方法: ### 3.1 配置依赖 首先,在项目的`pom.xml`文件中添加必要的依赖,包括数据库驱动、MyBatis-Plus核心依赖、代码生成器依赖以及模板引擎依赖。以下是一个示例配置: ```xml <dependencies> <!-- MySQL连接驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <!-- MyBatis-Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.1</version> </dependency> <!-- MyBatis-Plus Generator --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.1</version> </dependency> <!-- Freemarker模板引擎 --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.28</version> </dependency> <!-- JUnit单元测试依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> ``` ### 3.2 编代码生成类 接下来,编一个代码生成类,使用MyBatis-Plus的`AutoGenerator`工具来定义生成策略,并指定生成的单元测试代码的模板。以下是一个生成单元测试类的示例代码: ```java import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.TemplateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; public class TestCodeGenerator { public static void main(String[] args) { // 1. 配置数据源 DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true", "username", "password").build(); // 2. 全局配置 GlobalConfig globalConfig = new GlobalConfig.Builder() .outputDir(System.getProperty("user.dir") + "/src/test/java") .author("your_name") .fileOverride() .testFile() .build(); // 3. 包配置 PackageConfig packageConfig = new PackageConfig.Builder() .parent("com.example.demo") .moduleName("your_module") .build(); // 4. 策略配置 StrategyConfig strategyConfig = new StrategyConfig.Builder() .addInclude("your_table") // 指定生成的表名 .naming(NamingStrategy.underline_to_camel) .columnNaming(NamingStrategy.underline_to_camel) .entityLombokModel(true) .restControllerStyle() .build(); // 5. 构建代码生成器 AutoGenerator autoGenerator = new AutoGenerator(dataSourceConfig); autoGenerator.global(globalConfig); autoGenerator.packageInfo(packageConfig); autoGenerator.strategy(strategyConfig); // 6. 设置生成单元测试代码的模板 autoGenerator.template((templateType, javaClass) -> { if (templateType == TemplateType.TEST) { return "/templates/test.java.ftl"; // 自定义测试类模板路径 } return null; }); // 7. 执行生成 autoGenerator.execute(); } } ``` ### 3.3 自定义单元测试模板 在`resources/templates`目录下创建自定义的单元测试模板文件`test.java.ftl`,内容如下: ```java package ${package}; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest class ${testClass} { @Resource private ${serviceClass} ${service}; @Test void testSelect() { List<${entity}> list = ${service}.list(); assertNotNull(list); assertFalse(list.isEmpty()); } @Test void testInsert() { ${entity} entity = new ${entity}(); // 设置测试字段 assertTrue(${service}.save(entity)); } } ``` ### 3.4 优化建议 - **量操作优化**:如果测试过程中涉及大量数据插入,建议在数据库连接字符串中添加`rewriteBatchedStatements=true`,并使用MySQL驱动5.0.18以上版本,以提升量插入性能[^1]。 - **模板灵活性**:可以为同的业务场景创建多个模板,例如包含同的测试用例结构、Mockito模拟数据等。 - **集成CI/CD**:将代码生成流程集成到持续集成/持续部署(CI/CD)流程中,确保每次数据库结构变更后自动生成最新的测试代码。 通过上述方法,可以高效地使用MyBatis-Plus量生成单元测试代码,提高开发效率并减少重复劳动。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值