如何为代码设计创建单元测试?
作为一名经验丰富的软件工程师,我深知精心设计的代码的重要性,以及单元测试在维护代码完整性方面所发挥的作用。代码设计是任何软件系统的基础。它是指导系统构建的蓝图,确保最终产品的健壮性、可扩展性和可维护性。精心设计的代码库易于理解、修改和扩展,从而能够灵活地适应不断变化的业务环境需求。
一致的代码设计对于团队的生产力和项目的长期可维护性至关重要。然而,实现这一点并非易事。它需要所有团队成员清晰的沟通、共同的理解和严谨的纪律。难点在于每个开发人员都有其独特的编码风格和解决问题的方法。随着时间的推移,如果没有齐心协力保持一致性,代码库可能会变成各种风格和模式的拼凑物,使其难以理解和维护。
我想分享一种基于 ArchUnit 的方法——为您的代码进行单元测试。
ArchUnit 是由 TNG 开发的一个功能强大的库,它将架构控制权直接引入到你的代码库中。该项目的核心思想是验证 Java 代码库中的架构规则,确保遵循你设定的设计原则和标准。
该库提供了一种领域特定语言 (DSL),允许您以清晰、简洁的方式表达架构规则。这使得开发人员更容易理解和执行这些规则,从而实现更一致、更易于维护的代码库。
@AnalyzeClasses(packages = "com.tngtech.archunit.example.layers")
public class MethodsTest {
@ArchTest
static ArchRule codeUnitsInDAOLayerShouldNotBeSecured =
noCodeUnits()
.that().areDeclaredInClassesThat().resideInAPackage("..persistence..")
.should().beAnnotatedWith(Secured.class);
}
使用 ArchUnit 的主要优势之一是它能够及早发现架构违规。ArchUnit 无需等待代码审查或架构审计,只需在代码编写完成后即可发现问题。这可以更快地获得反馈并减少技术负担。
此外,ArchUnit 灵活且可扩展,允许您根据项目的独特需求定义自定义规则。它与 JUnit 等流行的测试框架无缝集成,非常适合任何 Java 项目。
例如,当您更喜欢 AssertJ 库时,您可以禁用 JUnit 断言:
@Test
void isTestClassesDontUseJunitAssertions() {
ArchRuleDefinition.noClasses()
.should()
.dependOnClassesThat()
.haveNameMatching("org.junit.jupiter.api.Assertions")
.check(importedTestClasses);
}
或者对于 Spring,您可以添加对控制器名称的限制:
@Test
void controllerNameRule() {
classes()
.that()
.areAnnotatedWith(RestController.class)
.should()
.haveSimpleNameContaining("Controller")
.check(importedClasses);
}
甚至可以检查 RequestBody 中的验证使用情况:
@Test
void restControllerValidationRule() {
classes()
.that()
.areAnnotatedWith(RestController.class)
.should()
.beAnnotatedWith(Validated.class)
.check(importedClasses);
}
@Test
void restControllerValidationRequestBodyRule() {
classes()
.that()
.areAnnotatedWith(RestController.class)
.should(
new ArchCondition<>("Any @RequestBody must be @Valid") {
@Override
public void check(JavaClass javaClass, ConditionEvents conditionEvents) {
for (JavaMethod method : javaClass.getMethods()) {
if (method.isConstructor()) {
continue;
}
for (Parameter parameter : method.reflect().getParameters()) {
if (parameter.isAnnotationPresent(RequestBody.class)
&& !parameter.isAnnotationPresent(Valid.class)) {
conditionEvents.add(
new SimpleConditionEvent(
javaClass,
false,
javaClass.getName()
+ " contains method "
+ method
+ " with @RequestBody and without Valid"));
}
}
}
}
})
.check(importedClasses);
}
当然,ArchUnit 的强大功能之一就是它的预定义规则。这些规则为在 Java 代码库中强制执行架构标准提供了坚实的基础。它们涵盖了各种常见的架构场景,让您可以更轻松地开始架构控制。
这些预定义规则可以按原样使用,也可以作为创建自定义规则的起点。这种灵活性使您可以根据项目的特定需求定制 ArchUnit,从而确保以对您的团队和代码库有意义的方式维护您的架构标准。
本质上,ArchUnit 的预定义规则是维护架构完整性、减少技术债务和提高代码整体质量的宝贵工具。它们证明了该库的强大功能和多功能性,使其成为任何认真对待架构的 Java 开发人员的必备工具。
您可以在 com.tngtech.archunit.library.GeneralCodingRules 中找到一般预定义规则,例如:
- 任何类都不应访问标准流
- 任何类都不应抛出通用异常
- 任何类都不应该使用 JODATIME
- 任何类都不应该使用 JAVA_UTIL_LOGGING
- 任何类都不应使用字段注入
com.tngtech.archunit.library.DependencyRules 中提供了依赖规则:
- 任何类都不应依赖于上层软件包
您可以在官方用户指南https://www.archunit.org/userguide/html/000_Index.html或 github 存储库https://github.com/TNG/ArchUnit中找到更多示例
总而言之,ArchUnit 是一款非常有价值的工具,可以维护代码库架构的完整性、提高一致性、减少技术债务并提升整体代码质量。对于任何认真对待架构的团队来说,它都是必备工具。
作者是 Mark Andreev,SWE@Conundrum.ai
鏂囩珷鏉ユ簮锛�https://dev.to/mrkandreev/how-to-create-unit-tests-for-code-design-5g9i