0

正如问题所暗示的,我如何使用 archUnit 检查某些导入。

因此,当测试类本身导入 lombok.experimental.* 时,我希望测试失败。

我了解如何检查包裹和类似的东西,但这种方法似乎不适用于进口。有什么建议么?

我的代码:

package com.nikita.Nikitos;

import static org.junit.Assert.assertTrue;

import org.junit.Test;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;

public class AppTest
{
    @Test
    public void keineKlassenAusLombokExperimental() {

        JavaClasses classes = new ClassFileImporter()
                .importPackages("com.nikita..");

        noClasses().should().dependOnClassesThat()
        .resideInAPackage("lombok.experimental..").check(classes);

    }

}

我要测试的课程:

package com.nikita.Nikitos;

import lombok.experimental.UtilityClass;

@UtilityClass
public class App
{
static int hd;
}
4

2 回答 2

1

导入不会在字节码中生成任何签名,因此 ArchUnit 无法直接检测到它。
检查您的代码是否不依赖于该软件包还不够吗?

ArchRule lombok_experimental_is_not_used = noClasses()
        .should().dependOnClassesThat().resideInAPackage("lombok.experimental..");

如果您只想检测星号导入,那么很遗憾您需要使用其他工具。

于 2022-01-21T16:33:30.293 回答
1

Lombok充当修改您的类的注释处理器。

在这种情况下@lombok.experimental.UtilityClass(可能还有其他 lombok 注释),最终的字节码实际上不再包含注释:

@lombok.experimental.UtilityClass
public class App {
    static int hd;
}

被编译(转换)为

public final class App
  flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
  this_class: #5                          // App
  super_class: #6                         // java/lang/Object
  interfaces: 0, fields: 1, methods: 1, attributes: 1
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Class              #16            // java/lang/UnsupportedOperationException
   #3 = String             #17            // This is a utility class and cannot be instantiated
   #4 = Methodref          #2.#18         // java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
   #5 = Class              #19            // App
   #6 = Class              #20            // java/lang/Object
   #7 = Utf8               hd
   #8 = Utf8               I
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               SourceFile
  #14 = Utf8               App.java
  #15 = NameAndType        #9:#10         // "<init>":()V
  #16 = Utf8               java/lang/UnsupportedOperationException
  #17 = Utf8               This is a utility class and cannot be instantiated
  #18 = NameAndType        #9:#21         // "<init>":(Ljava/lang/String;)V
  #19 = Utf8               App
  #20 = Utf8               java/lang/Object
  #21 = Utf8               (Ljava/lang/String;)V
{
  static int hd;
    descriptor: I
    flags: (0x0008) ACC_STATIC

  private App();
    descriptor: ()V
    flags: (0x0002) ACC_PRIVATE
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: new           #2                  // class java/lang/UnsupportedOperationException
         7: dup
         8: ldc           #3                  // String This is a utility class and cannot be instantiated
        10: invokespecial #4                  // Method java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
        13: athrow
      LineNumberTable:
        line 4: 0
}

也可以从这个纯 Java 代码生成:

public final class App {
    static int hd;

    private App() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }
}

如果你想用 ArchUnit 检测字节码中的这种模式,你可能不得不对 Lombok 所做的事情进行逆向工程,例如在调用UnsupportedOperationException(String)构造函数的最终类中搜索私有构造函数:

ArchRule no_UtilityClass = noConstructors()
    .should().bePrivate()
    .andShould().beDeclaredInClassesThat().haveModifier(JavaModifier.FINAL)
    .andShould(new ArchCondition<JavaCodeUnit>("call new UnsupportedOperationException(String)") {
        @Override
        public void check(JavaCodeUnit codeUnit, ConditionEvents events) {
            boolean satisfied = codeUnit.getCallsFromSelf().stream().anyMatch(call ->
                    call.getTargetOwner().isEquivalentTo(UnsupportedOperationException.class)
                 && call.getName().equals(JavaConstructor.CONSTRUCTOR_NAME)
                 && call.getTarget().getRawParameterTypes().size() == 1
                 && call.getTarget().getRawParameterTypes().get(0).isEquivalentTo(String.class)
            );
            String message = String.format("%s %s `new UnsupportedOperationException(String)` in %s",
                    codeUnit.getDescription(), satisfied ? "calls" : "does not call", codeUnit.getSourceCodeLocation()
            );
            events.add(new SimpleConditionEvent(codeUnit, satisfied, message));
        }
    });

如果您想禁止lombok.experimental.*源代码中使用,很遗憾您需要另一个工具;ArchUnit(目前)只分析bytecode

于 2022-01-21T20:45:56.783 回答