当前位置:   article > 正文

Java IDEA JUnit 单元测试

idea junit

JUnit是一个开源的 Java 单元测试框架,它使得组织和运行测试代码变得非常简单,利用JUnit可以轻松地编写和执行单元测试,并且可以清楚地看到哪些测试成功,哪些失败

JUnit 还提供了生成测试报告的功能,报告不仅包含测试的成功率,还能统计被测试代码的覆盖率。通过进行单元测试,我们可以确保每个方法按照预期正确运行。

如果我们修改了某个方法的代码,只需要确保相应的单元测试通过,就可以认为修改是正确的。此外,测试代码本身也可以作为示例代码,用于演示如何调用该方法。

几乎所有的IDE工具都集成了JUnit,我们这里使用IDEA

参考 编写JUnit测试 - 廖雪峰的官方网站 (liaoxuefeng.com)

目录

编写JUnit单元测试

使用Fixture自动执行代码

异常测试

条件测试

参数化测试

编写JUnit单元测试

用递推的方法写一个计算n的阶乘的Java方法。

我们可以针对刚刚写的Java编写一个对应的测试代码对其进行测试,在IDEA中可以直接右击点击生成Junit测试。

点击确定生成一个FactorialTest.java文件。

这是JUnit会把带有@Test的方法识别为测试方法,因此需要给测试方法加上@Test注解,测试方法内部用assertEquals(1, Factorial.fact(1))表示期望Factorial.fact(1)返回1。

运行这个测试程序,JUnit就会给出成功的测试和失败的测试,还可以生成测试报告,不仅包含测试的成功率,还可以统计测试的代码覆盖率,即被测试的代码本身有多少经过了测试。

 Factorial.java

  1. public class Factorial {
  2. public static long fact(long n) {
  3. long r = 1;
  4. for (long i = 1; i <= n; i++) {
  5. r = r * i;
  6. }
  7. return r;
  8. }
  9. }

 FactorialTest.java

  1. import static org.junit.jupiter.api.Assertions.*;
  2. import org.junit.jupiter.api.Test;
  3. public class FactorialTest {
  4. @Test
  5. void testFact() {
  6. assertEquals(1, Factorial.fact(1));
  7. assertEquals(2, Factorial.fact(2));
  8. assertEquals(6, Factorial.fact(3));
  9. assertEquals(3628800, Factorial.fact(10));
  10. assertEquals(2432902008176640000L, Factorial.fact(20));
  11. }
  12. }

使用Fixture自动执行代码

Fixture是JUnit提供的编写测试前准备、测试后清理的固定代码,可以用于测试前和测试后自动执行代码。

先编写一个简单的实现加减法功能的Calculator代码。

但是测试的时候,需要先初始化对象,可以使用@BeforeEach和@AfterEach标记的方法,@BeforeEach标记的方法会在执行每个@Test的方法之前调用,而@AfterEach标记的方法会在执行每个@Test的方法之后调用,这样就可以通过@BeforeEach和@AfterEach标记来自动实现对象的生成和销毁。

然后再编写我们的测试代码。

运行测试代码,可以看到测试结果。

如果需要在所有@Test方法运行前后仅运行一次,那么可以使用@BeforeAll和@AfterAll对方法进行标记。

 Calculator.java

  1. public class Calculator {
  2. private long n = 0;
  3. public long add(long x) {
  4. n = n + x;
  5. return n;
  6. }
  7. public long sub(long x) {
  8. n = n - x;
  9. return n;
  10. }
  11. }

 CalculatorTest.java

  1. import static org.junit.jupiter.api.Assertions.*;
  2. import org.junit.jupiter.api.Test;
  3. import org.junit.jupiter.api.AfterEach;
  4. import org.junit.jupiter.api.BeforeEach;
  5. public class CalculatorTest {
  6. Calculator calculator;
  7. @BeforeEach
  8. public void setUp() {
  9. this.calculator = new Calculator();
  10. }
  11. @AfterEach
  12. public void tearDown() {
  13. this.calculator = null;
  14. }
  15. @Test
  16. void testAdd() {
  17. assertEquals(100, this.calculator.add(100));
  18. assertEquals(150, this.calculator.add(50));
  19. assertEquals(130, this.calculator.add(-20));
  20. }
  21. @Test
  22. void testSub() {
  23. assertEquals(-100, this.calculator.sub(100));
  24. assertEquals(-150, this.calculator.sub(50));
  25. assertEquals(-130, this.calculator.sub(-20));
  26. }
  27. }

异常测试

对于可能抛出的异常进行测试是测试的重要环节,因此在编写JUnit测试的时候,除了正常的输入输出,还要特别针对可能导致异常的情况进行测试。

在计算阶乘的方法中增加对参数n的检查,如果n为负数,则直接抛出异常IllegalArgumentException。

在测试代码中,我们可以编写一个@Test方法专门测试异常,JUnit提供assertThrows函数来期望捕获一个指定的异常。

运行测试代码,可以看到测试结果。

 Factorial.java

  1. public class Factorial {
  2. public static long fact(long n) {
  3. if (n < 0) {
  4. throw new IllegalArgumentException();
  5. }
  6. long r = 1;
  7. for (long i = 1; i <= n; i++) {
  8. r = r * i;
  9. }
  10. return r;
  11. }
  12. }

 FactorialTest.java

  1. import static org.junit.jupiter.api.Assertions.*;
  2. import org.junit.jupiter.api.Test;
  3. public class FactorialTest {
  4. @Test
  5. void testFact() {
  6. assertEquals(1, Factorial.fact(1));
  7. assertEquals(2, Factorial.fact(2));
  8. assertEquals(6, Factorial.fact(3));
  9. assertEquals(3628800, Factorial.fact(10));
  10. assertEquals(2432902008176640000L, Factorial.fact(20));
  11. }
  12. @Test
  13. void testNegative() {
  14. assertThrows(IllegalArgumentException.class, () -> {
  15. Factorial.fact(-1);
  16. });
  17. }
  18. }

条件测试

条件测试可以在满足某种条件下执行某些测试方法,不执行某些测试方法。

编写一个程序,该程序中的方法在Windows上跑和在Linux上跑的代码路径不同。

编写测试代码的时候,用@EnableOnOs标记方法,指定只有在特定系统下才执行该测试方法。

用@DisabledOnOs标记方法表示不在某个系统上执行该方法。

用@DisabledOnJre标记方法表示只能在高于特定Java版本的测试。

用@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")标记,表示只能在64位操作系统上执行的测试。

用@EnabledIfEnvironmentVariable标记方法表示需要传入环境变量DEBUG=true才能执行的测试。

运行测试代码,可以看到测试结果。

 Config.java

  1. public class Config {
  2. public String getConfigFile(String filename) {
  3. String os = System.getProperty("os.name").toLowerCase();
  4. if (os.contains("win")) {
  5. return "C:\\" + filename;
  6. }
  7. if (os.contains("mac") || os.contains("linux") || os.contains("unix")) {
  8. return "/usr/local/" + filename;
  9. }
  10. throw new UnsupportedOperationException();
  11. }
  12. }

 ConfigTest.java

  1. import static org.junit.jupiter.api.Assertions.*;
  2. import org.junit.jupiter.api.AfterEach;
  3. import org.junit.jupiter.api.BeforeEach;
  4. import org.junit.jupiter.api.Test;
  5. import org.junit.jupiter.api.condition.*;
  6. public class ConfigTest {
  7. Config config;
  8. @BeforeEach
  9. public void setUp() {
  10. this.config = new Config();
  11. }
  12. @AfterEach
  13. public void tearDown() {
  14. this.config = null;
  15. }
  16. @Test
  17. @EnabledOnOs(OS.WINDOWS)
  18. void testWindows() {
  19. assertEquals("C:\\test.ini", config.getConfigFile("test.ini"));
  20. }
  21. @Test
  22. @EnabledOnOs({OS.LINUX, OS.MAC})
  23. void testLinuxAndMac() {
  24. assertEquals("/usr/local/test.cfg", config.getConfigFile("test.cfg"));
  25. }
  26. @Test
  27. @DisabledOnOs(OS.WINDOWS)
  28. void testOnNonWindowsOs() {
  29. // TODO: this test is disabled on windows
  30. }
  31. @Test
  32. @DisabledOnJre(JRE.JAVA_8)
  33. void testOnJava9OrAbove() {
  34. // TODO: this test is disabled on java 8
  35. }
  36. @Test
  37. @EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
  38. void testOnlyOn64bitSystem() {
  39. // TODO: this test is only run on 64 bit system
  40. }
  41. @Test
  42. @EnabledIfEnvironmentVariable(named = "DEBUG", matches = "true")
  43. void testOnlyOnDebugMode() {
  44. // TODO: this test is only run on DEBUG=true
  45. }
  46. }

参数化测试

JUnit提供了一个@ParameterizedTest注解,用来进行参数化测试。参数化测试和普通测试稍微不同的地方在于,一个测试方法需要接收至少一个参数,然后,传入一组参数反复运行。

编写一个方法,该方法把字符串的第一个字母变为大写,后续字母变为小写。

在编写测试代码的时候,需要给出输入和预期输出,可以通过@MethodSource注解,它允许我们编写一个同名的静态方法来提供测试参数,编写一个静态方法testCapitalize返回了一组测试参数,每个参数都包含两个String,作为测试方法的两个参数传入。

还可以使用@CsvSource标记传入测试参数的方法,它的每一个字符串表示一行,一行包含的若干参数用 , 分隔。

如果测试数据很多,可以把测试数据提到一个独立的CSV文件中,标注上@CsvFileSource表示从CSV文件中读取数据。

由于JUnit只在classpath中查找指定的CSV文件,因此,test-capitalize.csv这个文件要放到src/main/resources目录下,内容格式如下图所示。

运行测试程序,测试结果如下图所示。

 StringUtils.java

  1. public class StringUtils {
  2. public static String capitalize(String s) {
  3. if (s.length() == 0) {
  4. return s;
  5. }
  6. return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
  7. }
  8. }

StringUtilsTest.java

  1. import org.junit.jupiter.params.ParameterizedTest;
  2. import org.junit.jupiter.params.provider.CsvFileSource;
  3. import static org.junit.jupiter.api.Assertions.*;
  4. public class StringUtilsTest {
  5. // @ParameterizedTest
  6. // @MethodSource
  7. // void testCapitalize(String input, String result) {
  8. // assertEquals(result, StringUtils.capitalize(input));
  9. // }
  10. //
  11. // static List<Arguments> testCapitalize() {
  12. // return List.of( // arguments:
  13. // Arguments.of("abc", "Abc"), //
  14. // Arguments.of("APPLE", "Apple"), //
  15. // Arguments.of("gooD", "Good"));
  16. // }
  17. // @ParameterizedTest
  18. // @CsvSource({"abc, Abc", "APPLE, Apple", "gooD, Good"})
  19. // void testCapitalize(String input, String result) {
  20. // assertEquals(result, StringUtils.capitalize(input));
  21. // }
  22. @ParameterizedTest
  23. @CsvFileSource(resources = {"test_capitalize.csv"})
  24. void testCapitalizeUsingCsvFile(String input, String result) {
  25. assertEquals(result, StringUtils.capitalize(input));
  26. }
  27. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/651192
推荐阅读
相关标签
  

闽ICP备14008679号