轻松上手:Java单元测试和JUnit的实践指南
Java 单元测试:
单元测试是开发者编写的一小段代码,用于检测被测代码的一个很小的、很明确的功能是否正确。
单元测试的方法:
人工静态分析:人工阅读检测代码
自动静态分析:使用代码复查工具检查
自动动态测试:用工具自动生成测试用例并执行被测程序
人工动态测试:人工设定程序的输入和预期输出,执行程序。
Junit 单元测试:
它是人工动态测试
支持语言C++、Java,支持的IDE: Eclipse
功能:用单独的classloader来运行每个单元测试
标准的资源初始化和回收方式(setUp、tearDown)
Eclipse 使用Junit测试的方法
1、导入 Junit Jar包,可以在Java Build Path 中添加,最好直接使用Eclipse 自带的JUnit套件
2、为单元测试代码创建单独的目录,单元测试代码和被测试代码建议使用一样的包层结构。比如src\com\lls\Person.java 和 testSrc\com\lls\PersonTest.java
3、创建单元测试类,可以直接通过Eclipse的Junit Test Case 功能进行添加,这样就只需修改Source folder即可
下面内容为Junit 4.0之后内容
4、Junix 包所定义的注解及其意义
方法注解
@Before - 它所修饰的方法在每个测试方法执行之前都要执行一次。 之前没有注解的版本叫做setUp方法
@After - 它所修饰的方法在每个测试方法执行之后都要执行一次。 tearDown方法
@Test - 测试用的方法,他可以添加参数来对特定的情况进行验证
比如@Test(expected=*.class) 表示的是异常的类型,如果没有产生异常则失败
@Test(timeout = xxx) 表示的是测试的最大时间,超时则认为失败
@ignore - 它所修饰的测试方法会被忽略
其中@Before 和@After 被称为Fixture,也就是每个测试之前都必须要执行的内容
5、测试方法使用要求
@Test
public void xxx(无参)
6、使用Eclipse的 Run as Juit来执行测试用例
如果与测试结果不符,则结果为 失败 failure
如果测试时,直接产生异常,则结果为error
7、测试套件
也就是一次执行多个测试用例
方法:创建一个空类作为测试套件的入口。使用注解RunWith和SuiteClasses修饰这个空类。
将org.junit.runners.Suite 作为参数传入注解RunWith,表示使用套件运行器执行。
将测试类组成数组作为注解SuiteClasses的参数
比如:
@RunWith(Suite.class)
@Suite.SuiteClasses({xx1.class, xx2.class})
public class RunAllUtilTestsSuite {
}
原理及框架说明
8、Runner 运行器
在运行测试代码的时候是通过Runner来运行(也就是Runner的子类),比如,默认的JUnit4ClassRunner(不过这个已经废弃了,看来有了新的)
它可以用@RunWith 注解来说明,需要注意的是它修饰的是类
里面包含两个特殊的测试,参数化测试、套件打包测试
参数化测试:
@RunWith(Parameterized.class)
首先应该为这种测试专门生成一个新的类,因为他要用一个新的运行器。然后指定运行器为Parameterized。
然后在测试类中定义两个变量,一个用于存放参数,另一个存放期待的结果。接下来定义测试数据的集合,用@Parameters标注进行修饰。
构造函数中对变量进行初始化,顺序需要与集合中的顺序一致,比如{参数,预期结果}。
打包测试:
通常在一个项目中,我们需要写很多个测试类,打包测试就是一次执行多个或所有的测试类.
@RunWith(Suite.class) // 指定运行器
@Suite.SuiteClass({..,..,..}) // 指定需要同时测试的类
待测试的类: package com.test; public class Calculator { private static int result;// 静态变量,用于存储运行结果 public void add(int n) { result = result + n; } public void substract(int n) { result = result - 1; // Bug: 正确的应该是 result = result - n; } public void multiply(int n) { } // 此方法尚未写好 public void divide(int n) { result = result / n; } public void square(int n) { result = n * n; } public void squareRoot(int n) { for (;;) ; // Bug : 死循环 } public void clear() { // 将结果清零 result = 0; } public int getResult(){ return result; } } // ------------- 基本测试 package com.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; public class CalculatorTest { private static Calculator calculator = new Calculator(); @Before public void setUp() throws Exception { calculator.clear(); } @After public void tearDown() throws Exception { } @Test public void testAdd() { calculator.add(2); calculator.add(3); assertEquals(5, calculator.getResult()); } @Test public void testSubstract() { calculator.add(2); calculator.add(3); assertEquals(5, calculator.getResult()); } @Ignore("not implemented yet") @Test public void testMultiply() { fail("Not yet implemented"); } @Test public void testDivide() { calculator.add(8); calculator.divide(2); assertEquals(4, calculator.getResult()); } @Test(expected = Exception.class) public void DivideByZero(){ calculator.divide(0); } } // ----------------- 多个参数测试 package com.test; import static org.junit.Assert.*; import java.util.Arrays; import java.util.Collection; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class SquareTest { Calculator calculator; private int param; private int result; public SquareTest(int param,int result) { this.param = param; this.result = result; } @Before public void setUp() throws Exception { calculator = new Calculator(); } @After public void tearDown() throws Exception { } @Parameters public static Collection data(){ return Arrays.asList( new Object[][]{{2,4},{0,0},{-2,4}}); } @Test public void testSquare() { calculator.square(param); assertEquals(calculator.getResult(),result ); } } // --------------- 打包测试 @RunWith(Suite.class) @Suite.SuiteClasses({CalculatorTest.class, SquareTest.class }) public class AllCalculator { }
推荐阅读
-
35 岁实现财务*,腾讯程序员手握2300万提前退休?-1000万房产、1000万腾讯股票、加上300万的现金,一共2300万的财产。有网友算了一笔账,假设1000万的房产用于自住,剩下1300万资产按照平均税后20-50万不等进行计算,大约花上26-60年左右的时间才能赚到这笔钱。也就是说,普通人可能奋斗一辈子,才能赚到这笔钱。在很多人还在为中年危机而惶惶不可终日的时候,有的人的35岁,就已经安全着陆,试问哪个打工人不羡慕?但问题是有这样财富积累必然有像样的实力做靠山。没有人可以不劳而获。 看到这里,肯定有人说,那么对于普通人来说,卷可能真就成了唯一的出路。但是卷也有轻松的卷,“偷懒”的卷法,对于程序员而言,刨除掉一时无法改掉的开会传统占用的大部分时间,如何把有限的时间和精力放在真正重要的架构设计、需求设计上,而不是重复的造*,编码、改bug、手动测试。因此在科技改变生活的今天,学会使用AI工具成为程序员们的必备技能。 以全栈式全自动的软件开发工具飞算SoFlu软件机器人为例,作为全球首款面向微服务架构设计和最佳实践的软件机器人,SoFlu软件机器人改变了原来手工编码的作业模式,通过可视化拖拽方式以及参数配置就能实现等同于编写复杂代码的业务逻辑,在设计业务逻辑时就完成了微服务应用开发,做到“业务即图,图即代码”,实现“软件开发,十倍提效”。 而作为飞算SoFlu软件机器人的一个重要组成部分,FuncGPT(慧函数)支持所有类型函数创建。通过自然语言描述Java函数需求,实时生成高质量、高可读性的Java函数代码。生成代码可直接复制到IDEA,或一键导入Java全自动开发工具函数库。 FuncGPT(慧函数)具有五大核心能力: ● 自然语言:通过自然语言即可生成函数,降低软件开发门槛。 ● 秒级函数开发:全自动秒级开发函数,秒级完成,效率千倍提升。 ● 一站式函数开发应用:函数生成即可直接应用,效率、安全有保障。 ● 质量可靠:生成的函数代码符合业界规范、具备良好的可读性,符合最优解。 ● 灵活生成:可以根据开发人员的具体需求生成并支持调整和修改。 以“将文件流转换为File对象”这个需求为例,以下是FuncGPT(慧函数)秒级生成的代码: / 类名:FileConverter // 函数名:convertStreamToFile // 函数功能:将文件流转换为File对象 // POM依赖包:无 import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public class FileConverter { /** * 将文件流转换为File对象 * @param inputStream 文件流 * @param filePath 文件路径 * @param fileName 文件名 * @return 转换后的File对象 * @throws IOException 如果发生I/O异常 */ public static File convertStreamToFile(InputStream inputStream, String filePath, String fileName) throws IOException { File file = new File(filePath + File.separator + fileName); // 创建File对象 try (FileOutputStream outputStream = new FileOutputStream(file)) { // 创建文件输出流 byte buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { // 从文件流读取数据并写入文件 outputStream.write(buffer, 0, bytesRead); } } return file; // 返回转换后的File对象 } } // 函数示例 // 将文件流转换为File对象示例 // 入参:inputStream,文件流 // 入参:filePath,文件路径 // 入参:fileName,文件名 // 出参:file,转换后的File对象 // 调用示例: // InputStream inputStream = new FileInputStream("example.txt"); // String filePath = "C:\\Users\\User\\Documents"; // String fileName = "example.txt"; // File file = FileConverter.convertStreamToFile(inputStream, filePath, fileName); // System.out.println(file.getAbsolutePath); // 输出结果:例如,将文件流转换为File对象后,文件的绝对路径为:C:\Users\User\Documents\example.txt // 则输出结果为:C:\Users\User\Documents\example.txt 通过分析,不难发现以上代码:
-
从零开始的小程序单元测试实践(附有避免陷阱和源代码跟踪指南)
-
从零开始的小程序单元测试实践(附有避免陷阱和源代码跟踪指南)
-
树莓派打造自家私人云盘实战教程(第二部分):Docker+NextCloud+Nginx 教你一步步建站并上手" - 利用 Docker 架设简易高效的环境部署指南 - Docker 在树莓派上的安装入门,一看就会 - 参考链接:树莓派Docker安装指南 - 探索免费私有云盘方案:NextCloud 与 OwnCloud 同源平台解析 无需文件加密功能的话,NextCloud 是我们的首选。它与 SeaFile 相比,更加符合需求。 对于照片和视频备份同步,Daemon Sync 显示出了出色的表现,安装过程简单易懂,不妨一试身手。 现在,让我们一起步入正文,动手实践搭建属于自己的私人云盘吧!
-
轻松上手学Java Swing:从基础到局部实践指南
-
如何轻松上手Java命令工具:jhat命令的实用指南
-
在Java中轻松添加和读取Excel中的公式操作指南
-
轻松上手:Java单元测试和JUnit的实践指南