<dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.8.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.8.0</version> <scope>test</scope> </dependency> </dependencies>然后,创建一个简单的计算器类,通常这里替换为你实际要测试的业务类:
public class SimpleCalculator { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } }然后在 /test 的相同目录下创建对应的测试类
import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.assertEquals; public class SimpleCalculatorTest { // 堆代码 duidaima.com // 在所有测试方法执行前,仅执行一次。这个方法需要是静态的。 @BeforeAll static void setup() { System.out.println("BeforeAll - 初始化共享资源,例如数据库连接"); } // 在所有测试方法执行后,仅执行一次。这个方法需要是静态的。 @AfterAll static void tearDown() { System.out.println("AfterAll - 清理共享资源,例如关闭数据库连接"); } // 在每个测试方法执行前,都会执行一次。用于设置测试方法所需的初始状态。 @BeforeEach void init() { System.out.println("BeforeEach - 初始化测试实例所需的数据"); } // 在每个测试方法执行后,都会执行一次。用于清理测试方法使用的资源。 @AfterEach void cleanup() { System.out.println("AfterEach - 清理测试实例所用到的资源"); } // 标注一个测试方法,用于测试某个功能。 @Test void testAddition() { System.out.println("Test - 测试加法功能"); SimpleCalculator calculator = new SimpleCalculator(); assertEquals(5, calculator.add(2, 3), "2 + 3 应该等于 5"); } // 再添加一个测试方法 @Test void testSubtraction() { System.out.println("Test - 测试减法功能"); SimpleCalculator calculator = new SimpleCalculator(); assertEquals(1, calculator.subtract(3, 2), "3 - 2 应该等于 1"); } }以上程序,可以看到 Junit 常用注解使用说明:
mvn test输出结果:
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running SimpleCalculatorTest BeforeAll - 初始化共享资源,例如数据库连接 BeforeEach - 初始化测试实例所需的数据 Test - 测试加法功能 AfterEach - 清理测试实例所用到的资源 BeforeEach - 初始化测试实例所需的数据 Test - 测试减法功能 AfterEach - 清理测试实例所用到的资源 AfterAll - 清理共享资源,例如关闭数据库连接 [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.058 s - in SimpleCalculatorTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------或者可以直接在 IDEA 中执行测试,如下:
public class MyStringUtil { public String reverse(String input) { if (input == null) { return null; } return new StringBuilder(input).reverse().toString(); } }在静态测试类中,我们使用 @Test 定义 3 个方法来尝试覆盖 reverse() 可能得多种情况:
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class MyStringUtilStaticTest { private MyStringUtil stringUtil = new MyStringUtil(); @Test void reverseString() { // 堆代码 duidaima.com // 反转字符串 'hello' assertEquals("olleh", stringUtil.reverse("hello")); } @Test void reverseEmptyString() { // 反转空字符串 assertEquals("", stringUtil.reverse("")); } @Test void handleNullString() { // 处理 null 字符串 assertEquals(null, stringUtil.reverse(null)); } }然后用动态测试来实现同样的测试用例:
import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import java.util.Arrays; import java.util.Collection; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.DynamicTest.dynamicTest; public class MyStringUtilDynamicTest { private MyStringUtil stringUtil = new MyStringUtil(); // 使用 @TestFactory 注解定义了一个动态测试工厂方法 reverseStringDynamicTests() // 工厂方法返回一个 Collection<DynamicTest> @TestFactory Collection<DynamicTest> reverseStringDynamicTests() { // 包含了 3 个动态测试用例,每个测试用例使用 dynamicTest() 方法创建 return Arrays.asList( dynamicTest("动态测试:反转字符串 'hello'", () -> assertEquals("olleh", stringUtil.reverse("hello"))), dynamicTest("动态测试:反转空字符串", () -> assertEquals("", stringUtil.reverse(""))), dynamicTest("动态测试:处理 null 字符串", () -> assertEquals(null, stringUtil.reverse(null))) ); } }在动态测试类中逻辑如下:
public class BankAccount { private double balance; public BankAccount(double balance) { this.balance = balance; } public void withdraw(double amount) { assert amount > 0 : "Amount must be positive"; assert amount <= balance : "Insufficient balance"; balance -= amount; assert balance >= 0 : "Balance can't be negative"; } public double getBalance() { return balance; } }在这个示例中,我们使用了 Java 中的断言(assertion)来实现契约式设计。具体来说:
3.更高质量的交付:这里就不必多说了,通过测试的代码和没有测试的代码,是完全不一样的。未经测试的代码根本不具备上生产的条件
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency>接下来,你需要选择一个SLF4J的实现,例如Logback或Log4j2,并将其添加到项目中。你可以在Maven或Gradle中添加以下依赖:
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>在代码中,你可以使用以下代码打印Hello World:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HelloWorld { private static final Logger logger = LoggerFactory.getLogger(HelloWorld.class); public static void main(String[] args) { logger.info("Hello World"); } }这将使用SLF4J打印一条信息,其中包含“Hello World”字符串。你可以在控制台或日志文件中查看此信息。
日志级别 | 内容 |
---|---|
TRACE | 用于跟踪程序的细节信息,通常用于调试。 |
DEBUG | 用于调试程序,输出程序中的详细信息,例如变量的值、方法的调用等。 |
INFO | 用于输出程序的运行状态信息,例如程序启动、关闭、连接数据库等。 |
WARN | 用于输出警告信息,表示程序可能存在潜在的问题,但不会影响程序的正常运行。 |
ERROR | 用于输出错误信息,表示程序发生了错误,包括致命错误。 |
不同的日志级别用于记录不同的信息。这样做的目的不仅可以减少不必要的日志输出和文件大小,还可以提供快速定位的能力,例如开发环境通常使用 TRACE、DEBUG 日志,生产环境通常使用 INFO,WARN 日志等。这些信息都可以在 logback.xml 日志配置文件里面配置。
<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>/var/log/myapp.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>/var/log/myapp.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>7</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="FILE" /> </root> </configuration>在此配置文件中,定义了两个 appender:
工具名称 | Github 地址 |
---|---|
FindBugs | https://github.com/findbugsproject/findbugs |
PMD | https://github.com/pmd/pmd |
Checkstyle | https://github.com/checkstyle/checkstyle |
SonarQube | https://github.com/SonarSource/sonarqube |
IntelliJ IDEA | https://github.com/JetBrains/intellij-community/ |
3.静态扫描工具只能检查代码是否符合特定的规范和标准,但无法确保代码的质量和可读性。
3.可以促进团队协作和学习,通过分享和讨论代码,可以提高开发人员的技能和知识,并提高团队的凝聚力和效率。