使用 JUnit 5 进行 Java 单元测试:最佳实践和技术说明

news2024/9/22 21:17:42

掌握 Java 单元测试:深入了解工具、最佳实践和技术,以确保代码的稳健性。增强软件可靠性并完美交付!

想要提升 Java 开发工作量?本指南探索 Java 测试领域,涵盖基础概念和高级技术。您将了解测试驱动开发 (TDD) 的重要性、JUnit 5 的设置和使用、用于验证行为的断言以及编写高质量测试的最佳实践。无论您是希望掌握基础知识的初学者,还是希望提高技能的专家,您都会找到有关 Java 测试的宝贵见解。

什么是 Java 单元测试?

单元测试的目的是隔离代码的“单元”并对其进行测试,以确保它们按预期工作。“单元”是应用程序中可测试的最小部分,通常是单个方法或类。这样,当测试失败时,很容易确定哪个部分或“单元”没有按预期工作。

但在深入研究单元测试的具体步骤之前,让我们先看看为什么要创建单元测试。

为什么要编写单元测试?

Java 开发人员经常需要手动测试代码以查看其是否按预期运行。编写单元测试可帮助您自动化此过程,并确保相同的测试在相同的初始条件下在相同的环境中运行。

单元测试有许多优点,包括:

  1. 轻松排除故障: JUnit 测试会揭示您的代码何时未按预期运行。这让您能够更轻松地识别主要错误或问题,防止它们升级并影响到您的生产版本。
  2. 启用代码重构:单元测试在您的代码发生更改时提供安全网,以便您可以放心地重构和修改它,确保它不会在您的软件中引入新的错误。
  3. 提高代码质量:单元测试鼓励开发人员编写更加模块化、可测试、可维护的代码。

虽然编写单元测试最初可能很耗时,但最终可以通过减少在开发过程后期修复错误和重新编写代码所花费的精力来减少总体开发时间。

测试驱动开发

测试驱动开发是一种软件开发实践,开发人员在编写代码之前先编写测试方法。其理念是先评估预期行为。在许多情况下,这使得实现实际行为变得更容易。也更难引入错误。您可以通过编写额外的测试来揭露有缺陷的代码行为,从而修复出现的任何错误。

TDD 过程通常涉及三个步骤:

  • 编写失败测试:描述应用程序的预期行为并据此编写测试用例。测试预计会失败。
  • 编写代码:下一步是编写一些代码以使测试通过。编写代码只是为了满足测试的要求,仅此而已。
  • 重构:寻找改进代码的方法,同时仍保持其功能。这可能包括简化代码、删除重复代码或提高其性能。

安装 JUnit 5

现在我们已经介绍了测试驱动开发的重要性和过程,我们可以探索如何设置最流行的 Java 测试框架之一 JUnit 5。

Maven

要在 Maven 中安装 JUnit 5,请在 pom.xml 文件中添加以下依赖项。

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency><!-- For running parameterized tests -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependencies>

Gradle

要在 Gradle 中安装和设置 JUnit 5,请在 build.gradle 文件中添加以下行。

test { useJUnitPlatform() }

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
}

JUnit 包

org.junit 和 org.junit.jupiter.api 都是 Java 单元测试包,提供编写和运行测试的支持。

但是 org.junit 是较旧的测试框架,它是在 JUnit 4 中引入的,而 org.junit.jupiter.api 是较新的 Java 软件测试框架,它是在 JUnit 5 中引入的。后者建立在 JUnit 4 的基础上,并添加了新特性和功能。JUnit 5 框架支持参数化测试、并行测试和 lambda 等功能。为了我们的目的,我们将使用 JUnit 5。

如何编写单元测试

我们可以通过添加 @Test 注释将方法标记为测试。标记为测试的方法应该是公共的。

在 JUnit 5 中,有两种方式可以使用断言方法(如 assertEquals、assertTrue 等):静态导入和常规导入。

静态导入允许您仅使用类的静态成员(例如方法),而无需指定类名。在 JUnit 中,静态导入通常用于断言方法。例如,您可以在使用静态导入语句后直接使用 assertEquals(expected, actual),而不是编写 Assert.assertEquals(expected, actual)。

import static org.junit.jupiter.api.Assert.*;
public class MainTest {
    @Test
    public void twoPlusTwoEqualsFalse() {
        int result = 2 + 2;
        assertEquals(4, result);
    }
}

断言Assert

JUnit 5 提供了几种内置断言方法,可用于验证被测代码的行为。断言只是一种将测试单元的输出与预期结果进行比较的方法。

在本文中,我们将介绍几种测试方法。考虑到测试驱动开发的思想,我们不会介绍任何这些情况的代码实现。相反,我们将讨论预期的行为和边缘情况(如果有),并在此基础上编写 JUnit 测试。

Assert.assertEquals() 和 Assert.assertNotEquals()

assertEquals 方法用于检查两个值是否相等。如果预期值等于实际值,则测试通过。

在示例中,我们正在测试“add”方法,该方法接受两个整数并返回它们的总和。

void threePlusFiveEqualsEight() 
{
    Calculator calculator = new Calculator();
    
    // syntax: assertEquals(expected value, actual value, message);
    assertEquals(8, calculator.add(3, 5));
}

在比较对象时,assertEquals 方法使用对象的“equals”方法来确定它们是否相等。如果没有重写“equals”方法,则它才会执行引用比较。例如,对两个字符串调用 assertEquals 将调用 string.equals(string) 方法。

请记住这一点,因为数组不会覆盖“equals”方法。调用 array1.equals(array2) 将仅比较它们的引用。因此,您不应使用 assertEquals 来比较数组或任何未覆盖 equals 方法的对象。如果您想比较数组,请使用 Arrays.equals(array1, array2),如果您想测试数组相等性,请使用 assertArrayEquals 方法。

Assert.assetSame()

此方法比较两个对象或值的引用。当两个对象具有相同的引用时,测试通过。否则,测试失败。

Assert.assertTrue() 和 Assert.assertFalse()

assertTrue 方法检查给定条件是否为真。仅当条件为真时,测试才会通过。在这里,我们正在测试 mod() 方法,该方法返回数字的模数。

@Test
void  mustGetPositiveNumber () { 
    // 语法:assertTrue(condition) 
    assertTrue(calculator.mod(-32) >= 0) 
}

类似地,仅当条件为假时,assertFalse 方法才会通过测试。

Assert.assertNull() 和 Assert.assertNonNull()

您可能已经猜到了,assertNull 方法需要 null 值。同样,assertNonNull 方法需要任何非 null 值。

Assert.assertArrayEquals()

之前我们提到,对数组使用 assertEquals 不会产生预期的结果。如果要逐个元素比较两个数组,请使用 assertArrayEquals。

测试治具

JUnit 测试装置是一组对象、数据或代码,用于准备测试环境并提供已知的测试起点。这包括测试特定代码单元所需的准备和清理任务。

BeforeEach 和 AfterEach

JUnit 中的 @BeforeEach 注释用于标记测试类中每个测试之前应执行的方法。@BeforeEach 注释用于在执行每个测试用例之前准备测试环境或设置任何必要的资源。

在前面的例子中,我们不需要在每个测试方法中实例化计算器对象,而是可以在单独的方法中实例化它,该方法将在测试运行器执行测试之前调用。

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class CalculatorTest {    
    Calculator calculator;
    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }
    
    @Test
    void twoPlusTwoEqualsFour() {
        assertEquals(4, calculator.add(2, 2));
    }
}

JUnit 中的 @AfterEach 注释用于标记测试类中每次测试后应执行的方法。@AfterEach 注释可用于清除任何资源(如数据库或网络连接)或重置测试用例执行期间创建的状态。

BeforeAll 和 AfterAll

JUnit 中的 @BeforeAll 和 @AfterAll 注释用于标记在所有执行的测试用例之前和之后应执行一次的方法。

@BeforeAll 方法的主要用例是设置任何全局资源或初始化需要供类中的所有测试用例使用的任何共享状态。例如,如果测试类需要数据库连接,则可以使用 @BeforeAll 方法创建可由所有测试用例共享的单个数据库连接。

更多断言

在介绍了 @AfterEach、@BeforeAll 和 @AfterAll 等注释之后,我们现在可以深入研究 JUnit 中的一些高级断言,从测试异常的技术开始。

测试异常

为了检查一段代码是否会引发异常,您可以使用 assertThrows,它将预期异常的类引用作为第一个参数,将要测试的代码片段作为第二个参数。

现在,假设我们要测试 Calculator 类的“divide”方法,该方法接受两个整数,将第一个数字除以第二个数字并返回一个 double 值。但是,如果第二个参数为零,它会引发异常 (ArithmeticException)。我们可以使用 assertThrows 方法来测试。

@Test
void  testDivision () { 
    assertThrows(RuntimeException.class, () -> calculator.divide(32, 0)); 
}

如果您运行上述测试,您会注意到测试通过了。如前所述,divide 方法将返回 ArithmeticException,但我们不会检查它。上面的代码有效,因为 assertThrows 只检查是否会抛出异常,而不管类型如何。

使用 assertThrowsExactly 来预期固定类型的错误。在这种情况下,最好使用 assertThrowsExactly。

 @Test
void testDivision () { 
    assertThrowsExactly(ArithmeticException.class, () -> calculator.divide(32, 0)); 
}

assertNotThrows 方法将可执行代码作为参数,并测试代码是否抛出任何异常。如果没有抛出异常,则测试通过。

测试超时

assertTimeout 方法允许您测试一段代码是否在指定的时间限制内完成。以下是 assertTimeout 的语法:

assertTimeout(持续时间超时,可执行文件可执行)

assertTimeout 在单独的线程中执行被测代码,如果代码在指定的时限内完成,则断言通过。如果代码完成所花的时间超过指定的时限,则断言失败,并将测试标记为失败。

    @Test
     void  testSlowOperation () { 
        assertTimeout(Duration.ofSeconds(1), () -> { 
            Thread.sleep(500); // 模拟一个缓慢的操作
        }); 
    }

assertTimeoutPreemptively 断言是一种方法,它允许您测试一段代码是否在指定的时间内完成,就像 assertTimeout 一样。唯一的区别是,assertTimeoutPreemptively 会在超出时间限制时中断执行。

动态测试

JUnit 中的动态测试是在运行时生成的测试,而不是预定义的测试。它们允许开发人员根据输入数据以编程方式生成测试。动态测试是使用 @TestFactory 注释实现的。注释为 @TestFactory 的方法必须返回通用类型 DynamicTest 的 Stream、Collection 或 Iterator。

在这里,我们正在测试一个减法方法,它返回第一个参数和第二个参数之间的差值。

    @TestFactory
    Stream<DynamicTest> testSubtraction() {
        List<Integer> numbers = List.of(3, 7, 14, 93);

        return numbers.stream().map((number) -> DynamicTest.dynamicTest("Test: " + number, () -> 
            assertEquals(number - 4, calculator.subtract(number, 4));
        ));
    }

参数化测试

参数化测试允许您编写单个测试方法并使用不同的参数多次运行该方法。当您想要使用不同的输入值或值组合来测试方法时,这会很有用。

要在 JUnit 5 中创建参数化测试,您可以使用 @ParameterizedTest 注释并使用 @ValueSource、@CsvSource、@MethodSource、@ArgumentsSources 等注释提供参数。

传递一个参数

@ValueSource 注释接受任意类型的单个值数组。在下面的示例中,我们正在测试一个函数,该函数检查给定的数字是否为奇数。在这里,我们使用 @ValueSource 注释来获取参数列表。测试运行器针对提供的每个值运行测试。

    @ParameterizedTest 
    @ValueSource(ints = {3, 9, 77, 191}) void testIfNumbersAreOdd ( int number) 
    { 
        assertTrue(calculator.isOdd(number), "检查: " + number); 
    }
     

传递多个参数

@CsvSource 注释以逗号分隔的参数列表作为输入,其中每行代表测试方法的一组输入。在下面提供的示例中,我们正在测试一种返回两个整数乘积的乘法方法。

    @ParameterizedTest 
    @CsvSource({"3,4", "4,14", "15,-2"}) void testMultiplication ( int value1, int value2) { 
        assertEquals(value1 * value2, calculator.multiply(value1, value2)); 
    }
     

传递 null 和空值

@NullSource 注释提供单个 null 参数。测试方法使用 null 参数执行一次。

@EmptySource 注释提供了一个空参数。对于字符串,此注释将提供一个空字符串作为参数。

此外,如果您想同时使用 null 和空参数,请使用 @NullAndEmptySource 注释。

传递枚举

当 @EnumSource 注释与参数化测试方法一起使用时,该方法将针对每个指定的枚举常量执行一次。

在下面给出的示例中,测试运行器针对枚举的每个值运行 testWithEnum 方法。

enum Color {
    RED, GREEN, BLUE
}

@ParameterizedTest
@EnumSource(Color.class)
void testWithEnum(Color color) {
    assertNotNull(color);
}

默认情况下,@EnumSource 包含指定枚举类型中定义的所有常量。您还可以通过指定以下一个或多个属性来自定义常量列表。

name 属性用于指定要包含或排除的常量名称,mode 属性用于指定是否包含或排除名称。

    enum ColorEnum {
        RED, GREEN, BLUE
    }

    @ParameterizedTest
    @EnumSource(value = ColorEnum.class, names = {"RED", "GREEN"}, mode = EnumSource.Mode.EXCLUDE)
    void testingEnums(ColorEnum colorEnum) {
        assertNotNull(colorEnum);
    }

在上面的例子中,测试用例将只运行一次(针对 ColorEnum.BLUE)。

从文件传递参数

在下面的示例中,@CsvFileSource 用于指定一个 CSV 文件(test-data.csv)作为 testWithCsvFileSource() 方法的参数源。CSV 文件包含三列,分别对应该方法的三个参数。

// .csv 文件的内容
// src/test/resources/test-data.csv 
// 10, 2, 12 
// 14, 3, 17 
// 5, 3, 8 

@ParameterizedTest 
@CsvFileSource(resources = "/test-data.csv") void testWithCsvFileSource (String input1, String input2, String expected) {
         int iInput1 = Integer.parseInt(input1);
         int iInput2 = Integer.parseInt(input2);
         int iExpected = Integer.parseInt(expected); 
        assertEquals(iExpected, calculator.add(iInput1, iInput2)); 
}
 

resources 属性指定相对于项目中 src/test/resources 目录的 CSV 文件路径。如有必要,您也可以使用绝对路径。

请注意,CSV 文件中的值始终被视为字符串。您可能需要在测试方法中将它们转换为适当的类型。

从方法传递值

@MethodSource 注释用于指定方法作为参数化测试方法的参数来源。当您想根据自定义算法或数据结构生成测试用例时,这会很有用。

在下面的例子中,我们正在测试 isPalindrome 方法,该方法以整数作为输入并检查该整数是否是回文。

static Stream<Arguments> generateTestCases () {
     return Stream.of( 
        Arguments.of(101, true ), 
        Arguments.of(27, false ), 
        Arguments.of(34143, true ), 
        Arguments.of(40, false ) 
    ); 
} 

@ParameterizedTest 
@MethodSource("generateTestCases") 
void testWithMethodSource ( int input, boolean expected) { 
    // isPalindrome(int number) 方法检查给定的
    // 输入是否为回文
    assertEquals(expected, calculator.isPalindrome(input)); 
}

自定义参数

@ArgumentsSource(不要与 ArgumentsSources 混淆)是一种注释,可用于为参数化测试方法指定自定义参数提供程序。自定义注释提供程序是一个为测试方法提供参数的类。该类必须实现 ArgumentsProvider 接口并重写其 provideArguments() 方法。

请考虑以下示例:

static class StringArgumentsProvider implements ArgumentsProvider {
    String[] fruits = {"apple", "mango", "orange"};

        @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
            return Stream.of(fruits).map(Arguments::of);
        }
    }

    @ParameterizedTest
    @ArgumentsSource(StringArgumentsProvider.class)
    void testWithCustomArgumentsProvider(String fruit) {
        assertNotNull(fruit);
    }

在此示例中,StringArgumentsProvider 是一个自定义参数提供程序,它提供字符串作为测试参数。该提供程序实现 ArgumentsProvider 接口并重写其 provideArguments() 方法以返回参数流。

您可以使用@ArgumentsSources注释为单个参数化测试方法指定多个参数源。

嵌套测试

在 JUnit 5 中,嵌套测试类是一种将相关测试分组并以层次结构组织起来的方法。每个嵌套测试类都可以包含自己的设置、拆卸和测试。

要定义嵌套测试类,请在内部类前使用 @Nested 注释。内部类不应是静态的。

class ExampleTest {
    
    @BeforeEach
    void setup1() {}

    @Test
    void test1() {}

    @Nested
    class NestedTest {

        @BeforeEach
        void setup2() {}

        @Test
        void test2() {}

        @Test
        void test3() {}
    }
}

代码将按照以下顺序执行。

setup1() -> test1() -> setup1() -> setup2() -> test2() -> setup1() -> setup2() -> test3()

就像测试类可以包含嵌套测试类一样,嵌套测试类也可以包含自己的嵌套测试类。这样您就可以为测试创建层次结构,从而更轻松地组织和维护测试代码。

测试套件

JUnit 测试套件是一种组织测试的方法。虽然嵌套测试是一种很好的组织测试的方法,但随着项目复杂性的增加,维护它们变得越来越困难。此外,在运行任何嵌套测试方法之前,所有测试装置都会先运行,这可能是不必要的。因此,我们定期使用测试套件来组织我们的测试。

为了使用 JUnit 测试套件,首先创建一个新类(例如 ExampleTestSuite)。然后,添加 @RunWith(Suite.class) 注释以告诉 Junit 测试运行器使用 Suite.class 来运行测试。JUnit 中的 Suite.class 运行器允许您将多个测试类作为整个测试套件运行。然后,使用 @SuiteClasses 注释指定要运行的类。

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({
    CalculatorTest.class,
    CalculatorUtilsTest.class
})
public class CalculatorTestSuite {}

编写更好测试的最佳实践

现在我们已经探索了具体的断言,我们应该介绍可以最大程度提高测试效率的最佳实践。基本准则是保持测试简单而有针对性,但还有其他注意事项。让我们深入了解编写强大、高效的单元测试时要遵循的一些关键原则。

  • 编写简单而有针对性的测试:单元测试应该简单,每次只专注于测试代码的一个方面。它应该易于理解和维护,并且应该对正在测试的内容提供清晰的反馈。
  • 使用描述性测试名称:测试名称应具有描述性,并应提供有关正在测试的内容的清晰信息。这有助于使测试套件更具可读性和可理解性。要命名测试,请使用 @DisplayName 注释。
    @Test 
    @DisplayName("检查九加七是否等于十六")
    void twoPlusTwoEqualsFour () { 
        assertEquals(16, calculator.add(9,7)); 
    }
     
  • 在运行时使用随机值:在单元测试中不建议在运行时生成随机值。使用随机值有助于确保被测试的代码是稳健的,并且可以处理各种各样的输入。随机值有助于发现静态测试用例中可能不明显的极端情况和其他场景。但是,使用随机值也会降低测试的可靠性和可重复性。如果多次运行相同的测试,每次可能会产生不同的结果,这会使诊断和修复问题变得困难。如果使用随机值,记录用于生成它们的种子非常重要,这样才能重现测试。
  • 永远不要测试实现细节:单元测试应该专注于测试单元或组件的行为,而不是其实现方式。测试实现细节会使测试变得脆弱且难以维护。
  • 边缘情况:边缘情况是指您的代码可能失败的情况。例如,如果您正在处理对象,则一个常见的边缘情况是对象为空。确保在编写测试时涵盖所有边缘情况。
  • 安排-执行-断言 (AAA) 模式: AAA 模式是一种用于构建测试的有用模式。在此模式中,安排阶段设置测试数据和上下文,执行阶段执行正在测试的操作,断言阶段验证是否获得了预期结果。

Mockito

Mockito是一个开源 Java 模拟框架,允许您在单元测试中创建和使用模拟对象。模拟对象用于模拟系统中难以单独测试的真实对象。

安装

要将 mockito 添加到您的项目,请在 pom.xml 中添加以下依赖项。

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.3.0</version>
    <scope>test</scope>
</dependency>

如果您正在使用 Gradle,请将以下内容添加到您的 build.gradle。

repositories { mavenCentral() }
dependencies { testImplementation "org.mockito:mockito-core:3.+" }

在单元测试中,我们希望独立于系统其余部分测试代码单元的行为。但是,有时代码模块依赖于其他模块或一些外部依赖项,这些依赖项很难或无法单独测试。在这种情况下,我们使用模拟对象来模拟这些依赖项的行为并隔离受测模块。

在下面给出的示例中,我们有一个要测试的 User 类。User 类依赖于 UserService 类,后者负责从数据库获取数据。UserService 类有一个名为 getUserById 的方法,该方法从数据库获取有关用户的信息并返回。

public  class  User {
   private  final  int id;
   private  final UserService userService; public User ( int id, UserService userService) {
     this .userService = userService;
     this .id = id; 
  } 

  public String getName () { 
    UserInfo info = userService.getUserById(id); return info.getName(); 
  } 
}

public class UserService {
   public UserInfo getUserById ( int id) { 
     // 从数据库检索用户信息
   } 
}

为了对 User 类的 getName() 方法进行单元测试,我们需要独立于 UserService 类和数据库进行测试。

一种方法是使用模拟对象来模拟 UserService 类的行为。以下是使用 Mockito 执行此操作的示例:

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

@Test
public void testGetName() {
  UserService userService = Mockito.mock(UserService.class);

  UserInfo info = new UserInfo(123, "John");
  Mockito.when(userService.getUserById(123)).thenReturn(entity);

  User user = new User(123, userService);

  String name = user.getName();
  assertEquals("John", name);

  Mockito.verify(userService).getUserById(123);
}

在上面的例子中,我们使用 Mockito.mock() 方法为 UserService 类创建了一个模拟对象。然后我们使用 Mockito.when() 方法定义模拟对象的行为,该方法指定当使用参数 123 调用 getUserById() 方法时,模拟对象应返回名为“John”的 UserEntity 对象。

然后,我们使用模拟 UserService 创建一个 User 对象并测试 getName() 方法。最后,我们使用 Mockito.verify() 方法验证模拟对象是否正确使用,该方法检查是否使用参数 123 调用了 getUserById() 方法。

以这种方式使用模拟对象使我们能够独立于 UserService 类和数据库测试 getName() 方法的行为,确保任何错误或缺陷仅与 User 类本身的行为有关。

Java 测试框架

说到测试框架, JUnit是目前最受欢迎的选择。但是,还有很多其他选择。以下是其中一些:

  1. TestNG: TestNG是另一个流行的 Java 测试框架,支持广泛的测试场景,包括单元测试、功能测试和集成测试。它提供并行测试、测试依赖项和数据驱动测试等高级功能。
  2. AssertJ: AssertJ是一个 Java 断言库,提供用于定义断言的流畅 API。它提供用于测试不同类型的对象的各种断言,并支持自定义断言。
  3. Hamcrest: Hamcrest是一个 Java 断言库,它提供了多种匹配器来测试不同类型的对象。它允许开发人员使用自然语言断言编写更具表现力和可读性的测试。
  4. Selenium: Selenium是一个用于测试 Web 应用程序的 Java 测试框架。它允许开发人员使用多种编程语言(包括 Java)为 Web 应用程序编写自动化测试。
  5. Cucumber: Cucumber是一个 Java 测试框架,允许开发人员以行为驱动开发 (BDD) 风格编写自动化测试。它提供了一种简单的自然语言语法来定义测试,使编写易于阅读和理解的测试变得更加容易。

结论

在本文中,我们介绍了使用 JUnit 和 Mockito 进行单元测试所需了解的所有内容。我们还讨论了测试驱动开发的原则以及为什么要遵循它。

通过采用测试驱动开发方法,您可以确保代码按预期运行。但与任何软件开发实践一样,TDD 也有其优点和缺点,其有效性取决于具体项目和团队。对于较大的项目,聘请Java 开发服务可以提供测试专业知识,以根据您的需求正确实施 TDD。

最终,使用 TDD 的决定应考虑项目目标、团队技能以及外部 Java 测试资源是否有益。只要正确理解 TDD 权衡,即使是缺乏经验的团队也能从测试优先方法中获益。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2036933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Wireshark分析工具

简单用例 首先打开软件,左上角点文件,选中要分析的文件列表。 导入用tcpdump抓的包后进行分析,这里要输入过滤条件,对网络包进行一定的过滤处理。(这里172网段是阿里云的地址,用自己写的python2脚本对阿里云进行压测。) 这里输入过滤条件 tcp.port == 80 ,语法含义是…

快速把文件名统计到excel表的方法

文件名统计到EXCEL表&#xff0c;这似乎很多人都没听说过&#xff0c;因为它与EXCEL表格不沾边&#xff0c;那么这个需求如何实现&#xff0c;用到什么方法&#xff0c;今天给大家介绍一个比较实用的方法&#xff0c;它可以把文件名或文件夹的名快速提取并统计到EXCEL表格上去。…

C++ | Leetcode C++题解之第334题递增的三元子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:bool increasingTriplet(vector<int>& nums) {int n nums.size();if (n < 3) {return false;}int first nums[0], second INT_MAX;for (int i 1; i < n; i) {int num nums[i];if (num > s…

Unity 资源分享 之 恐龙Ceratosaurus资源模型携 82 个动画来袭

Unity 资源分享 之 恐龙Ceratosaurus资源模型携 82 个动画来袭 一、前言二&#xff0c;资源包内容三、免费获取资源包 一、前言 亲爱的 Unity 开发者和爱好者们&#xff0c;大家好&#xff01;今天要为大家分享一份超级酷炫的 Unity 资源——恐龙资源模型&#xff0c;而且它还…

HBase snapshot+replication 测试

一、背景 画像标签服务&#xff08;CDP&#xff09;是核心服务&#xff0c;被公司其他系统如现金、电商、风控等核心业务调用。异常的话&#xff0c;影响范围大。 二、目标 存量数据测试通过 snapshot 迁移。增量数据测试通过 replication 同步。 三、测试 方案二测试&#x…

LabVIEW电流互感器测试系统

开发了基于LabVIEW的光电式电流互感器测试系统的开发过程。通过系统设计、硬件选型及软件实现的详细描述&#xff0c;展示了该系统如何有效地进行电流互感器的性能测试。 项目背景 电流互感器是电力系统中不可或缺的一部分&#xff0c;用于监测和控制电流流动&#xff0c;确保…

Ubuntu基础使用-适合新手的讲解

目录 一、基础操作 1. 终端重要热键 2. 基础命令 3. 通配符 二、文件与目录 1. 2.等价 3.帮助指令 4.当前路径 更改路径 5.创建与删除文件夹 6.创建与删除文件 7.重定向&#xff08;写入&#xff0c;追加写入&#xff09; 8.查看文件内容 9.wc指令 10.复制&…

Haproxy知识点

目录 负载均衡 四层负载均衡 七层负载均衡 HAproxy简介 Haproxy的基本配置 haproxy基本部署 haproxy热处理 SOCAT工具 haproxy的算法 haproxy的静态算法 static-rr 基于权重的轮询调度 first haproxy的动态算法 roundrobin leastconn 其他算法 SOURCE url_pa…

微调大语言模型知识

在自然语言处理领域&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;展示了卓越的能力。了解这些模型的特点及微调方法可以帮助更好地应用它们。 大语言模型概述 大语言模型的定义 大语言模型&#xff08;LLM&#xff09;是用于理解和生成自然语言…

大语言模型与多模态大模型loss计算

文章目录 前言一、大语言模型loss计算1、loss计算代码解读2、构建模型输入内容与label标签3、input_ids与labels格式 二、多模态大模型loss计算方法1、多模态loss计算代码解读2、多模态输入内容2、大语言模型输入内容3、图像embending如何嵌入文本embeding 前言 如果看了我前面…

C++ 适配器 stack

不了解适配器的读者可以先去看一看这一篇博客&#xff1a;C 适配器-CSDN博客 栈数据结构介绍 我们平时洗碗的时候&#xff0c;叠成一层层的碗最后只能从上面往下面开始拿&#xff1b;平时箱子里面叠的衣服&#xff0c;也是只能从上面慢慢拿。先放进去的东西后拿出来&#xff…

【STM32嵌入式系统设计与开发拓展】——15_ADC

一、问题集结 1、ADC&#xff1f; (Analog to Digital Converter)即模数转换器&#xff0c;用来将模拟信号转换为数字信号. 模数转换器 2、什么是模拟量&#xff1f; 如温度&#xff0c;湿度等随着时间逐步逐步连续变化的量 3、什么是数字量&#xff1f; 如我们单片机处理的信…

Spring自动注册-自定义标签解析

创建实体 定义一个XSD文件描述组件内容 创建BeanDefinitionParser接口的实现类&#xff0c;用来解析XSD文件中的定义和组件定义。 创建NamespaceHandlerSupport实现类&#xff0c;目的是将组件注册到Spring容器中。 编写spring.handlers和spring.schemas文件&#xff0c;默认位…

Layui——实现弹出层中的form表单验证(lay-verify不生效)

目录 分析原因 完整版修改后的代码 完整版原代码 参考文章&#xff1a; 1、http://t.csdnimg.cn/64KdG2、http://t.csdnimg.cn/CUTPa3、http://t.csdnimg.cn/iHAIO 分析原因 添加部门时&#xff0c;部门名称不能为空&#xff0c;添加不成功&#xff0c;也没有任何提示&#…

使用ITextRenderer导出PDF后无法打开问题,提示‘无法打开此文件‘

依赖如下 <!-- https://mvnrepository.com/artifact/org.xhtmlrenderer/flying-saucer-pdf --> <dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf</artifactId><version>9.1.22</version> &l…

96、QT 实现LED圆形指示灯控件

代码实现更改控件样式表的颜色和形状等 //QLabel控件变成圆形LED指示灯样式表 const QString QLabel_LED_Circle_SheetStyle_Red "min-width: 20px; min-height: 20px;max-width:20px; max-height: 20px;border-radius: 10px; background:red";//红色 const QStrin…

[C++][opencv]基于opencv实现photoshop算法色相和饱和度调整

【测试环境】 vs2019 opencv4.8.0 【效果演示】 【核心实现代码】 HSL.hpp #ifndef OPENCV2_PS_HSL_HPP_ #define OPENCV2_PS_HSL_HPP_#include "opencv2/core.hpp" using namespace cv;namespace cv {enum HSL_COLOR {HSL_ALL,HSL_RED,HSL_YELLOW,HSL_GREEN,HS…

告别繁琐!2024年PDF转CAD工具大推荐

如果你是装修设计师收到的却是被阉割了的PDF&#xff0c;不要破防。现在有不少工具可以直接实现PDF转CAD再也不需要你费心重画哦。今天&#xff0c;我们就来聊聊2024年大家都在用的几款PDF转CAD工具&#xff0c;看看它们是如何改变我们的设计工作的。 1.福昕PDF转换大师 链接…

springboot整合mybatis以及mybatis-plus 开发

一、springboot整合mybatis 1.注解版 1.1 导入坐标 <dependencies><!--mybatis坐标--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</…

39、Python之面向对象:对象属性解析:MRO不够用,补充3个方法

引言 在前面的文章中&#xff0c;我们谈及Python在继承关系&#xff0c;尤其是多继承中&#xff0c;一个对象的属性的查找解析顺序。由于当时的语境聚焦于继承关系&#xff0c;所以只是简要提及了属性解析顺序同方法的解析顺序&#xff0c;而方法的解析顺序&#xff0c;在Pyth…