下面是针对"Java程序员如何编写更好的单元测试的7个技巧"的一份攻略。
技巧1:拆分单元测试
单元测试应该足够小,以至于一个单元测试只需要测试一个方法或函数。这样使得测试容易重复、快速执行和简单调试。拆分单元测试也使测试更精确,因为每个单元测试只测试一个输入和输出组合。
示例:
以下是一个简单的 Java 类,将两个整数相加并返回结果:
public class Addition {
public static int add(int a, int b) {
return a + b;
}
}
对于此类,将它拆分成两个单元测试,一个测试方法参数都为正数的情况,另一个测试方法参数一个为正数一个为负数的情况:
@Test
public void testAddition_positiveNumbers() {
int result = Addition.add(2, 4);
assertEquals(6, result);
}
@Test
public void testAddition_mixedNumbers() {
int result = Addition.add(2, -4);
assertEquals(-2, result);
}
技巧2:引入测试数据工厂
在编写单元测试时,我们需要为每个测试方法提供各种输入,并检查其输出。如果您手动编写每个测试用例,这将非常困难和耗费时间。测试数据工厂是测试单元函数时的好伙伴。它内置于测试库中,其中包含一些基本的测试函数和生成测试数据的工具类。
示例:
以下是一个 Java 类,将两个字符串连接在一起并返回结果:
public class Concatenation {
public static String concat(String a, String b) {
return a + " " + b;
}
}
下面是一个使用 Junit5 @ParameterizedTest 注释和 ArgumentsProvider 接口的示例,编写了一个 Factory 类为此类提供测试数据:
public class ConcatenationTest {
@ParameterizedTest
@ArgumentsSource(ConcatenationFactory.class)
void should_concatenate_strings(final String a, final String b, final String expected) {
final String result = Concatenation.concat(a, b);
assertEquals(expected, result);
}
private static class ConcatenationFactory implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(final ExtensionContext extensionContext) {
return Stream.of(
Arguments.of("Hello", "world", "Hello world"),
Arguments.of("JUnit5", "is awesome", "JUnit5 is awesome"),
Arguments.of("", "", " ")
);
}
}
}
技巧3:使用行为驱动测试
在单元测试中,最好使用行为驱动测试(BDD)或使用 AssertJ 等库的断言来描述测试。使用 BDD,您可以清楚地表达正在测试的行为或功能,并且确保测试的理解和可读性。
示例:
下面是一个使用 AssertJ 的 BDD 风格的示例:
@Test
void whenSalaryIsLessThanThreshold_outputIsZero() {
// Given
int salary = 20_000;
// When
int tax = calculateTax(salary);
// Then
assertThat(tax).isZero();
}
@Test
void whenSalaryIsGreaterThanThreshold_outputIsGreaterThanZero() {
// Given
int salary = 70_000;
// When
int tax = calculateTax(salary);
// Then
assertThat(tax).isGreaterThan(0);
}
技巧4:使用 MockObject 或 Stubbing 单元测试
在单元测试中,您必须仅测试功能模块。但是,如果模块依赖于其他模块,则必须针对其依赖项运行测试,并使用 MockObject 或 Stubbing 技术,该技术允许您在不执行外部依赖项的情况下测试。它有助于减少测试运行时间并提高测试速度。
示例:
以下类使用一个 GreetingsService 类,用于获取问候语并添加到消息中:
public class MessageService {
private final GreetingsService greetingsService;
public MessageService(final GreetingsService greetingsService) {
this.greetingsService = greetingsService;
}
public String getMessage(final String name) {
final String greeting = greetingsService.getGreeting(name);
return "Hello, " + name + "! " + greeting;
}
}
在以下测试中,我们使用 Mockito 来模拟 GreetingsService 的依赖项和 Stubbing:
@Test
void getMessage_ShouldReturnCorrectMessage() {
final GreetingsService mockedGreetingService = mock(GreetingsService.class);
final MessageService messageService = new MessageService(mockedGreetingService);
final String name = "Alice";
final String expected = "Hello, Alice! good morning";
when(mockedGreetingService.getGreeting(name)).thenReturn("good morning");
final String result = messageService.getMessage(name);
assertThat(result).isEqualTo(expected);
}
技巧5:运行单元测试的多个版本
随着时间的推移,您的代码库可能会发生变化。您应该保持您的测试套件不仅能够运行最新版本的代码,也要能够运行先前版本的代码。创建多个分支,每个分支对应于一个版本的代码,以确保它们仍容易编译,运行以及产生正确的结果。
示例:
假设我们有一个简单的 Java 类,将两个整数相加并返回值:
public class Addition {
public static int add(int a, int b) {
return a + b;
}
}
我们需要在代码库多个版本中运行测试。使用 Maven 的 profile 功能,您可以轻松地在支持的不同版本中运行测试,如下面的示例:
<profiles>
<profile>
<id>jdk11</id>
<activation>
<jdk>11</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
<configuration>
<argLine>--illegal-access=deny</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>jdk8</id>
<activation>
<jdk>1.8</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>--illegal-access=permit</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
技巧6:编写可读的单元测试
当您编写可读性差的测试时,您很难理解测试的意图。同时,当测试失败时,您需要知道出现了哪种问题,而可读性差的测试将会使问题更加难以定位。为了测试易读性,您可以编写代码注释、使用描述性的变量名、等等。
示例:
以下是一个使用 Junit5 和 AssertJ 的示例,它禁用了测试方法。使用注释和描述性变量名称,提高了测试可读性:
@Disabled("needs to be refactored")
@Test
void calculatingTaxForPersonThatMakesLessThan16k_shouldReturnZero() {
final int personSalary = 12_000;
final int personAge = 40;
final String personName = "John Doe";
final Person person = new Person(personName, personAge, personSalary);
final int result = taxCalculator.calculate(person);
assertThat(result).isEqualTo(0);
}
技巧7:在IDE中运行单元测试
在 IDE 中遵循 TDD(测试驱动开发)流程,可以让您快速反馈并调试功能问题,而不必延迟到将代码提交到代码库中后检查问题。许多 IDE 都支持单击按钮即可运行单元测试。
示例:
当使用 IntelliJ IDEA 编辑器时,点击 Test 按钮或使用快捷键 Ctrl + Shift + F10 即可运行单元测试。如果您更改代码,并保存文件,IntelliJ IDEA 将自动重新运行相关测试。
这就是“Java程序员如何编写更好的单元测试的7个技巧”的攻略,希望对您的测试工作有所帮助!
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java程序员如何编写更好的单元测试的7个技巧 - Python技术站