是否可以使 JUnit5 扩展实现由扩展 class 实现的接口?

Is it possible to make a JUnit5 Extension implement an interface that is fulfilled by the extended class?

我想写一个 JUnit5 Extension 来扩展我的测试 class,

@ExtendWith(MyExtension.class)
public class MyTestClass {

  @Test myTest1() {}

  @Test myTest2() {}

  // ...
}

不过,我的测试class也实现了某个接口,所以看起来更像这样:

public interface SomeInterface {
  SomeClient getSomeClient();
  SomeClient getSomeClientAsAdministrator();
}

@ExtendWith(MyExtension.class)
public class MyTestClass implements SomeInterface {

  @Test myTest1() {}

  @Test myTest2() {}

  // ...

  SomeClient getSomeClient() {
    // ...
  }

  SomeClient getSomeClientAsAdministrator() {
    // ...
  }
}

目前没有任何谜团。

但是现在,我希望这些接口实现也可用于扩展,例如

public class MyExtension implements BeforeEachCallback, SomeInterface
{

  @Override
  public void beforeAll(ExtensionContext extensionContext) {
    // be able to use getSomeClient();
  }
}

如何设置我的 classes 以实现此目的? (或者,反对这样做的固有缺陷或代码味道是什么?)

您需要使用允许您手动构建扩展实例的 @RegisterExtension 注释。

When an extension is registered declaratively via @ExtendWith, it can typically only be configured via annotations. In contrast, when an extension is registered via @RegisterExtension, it can be configured programmatically — for example, in order to pass arguments to the extension’s constructor, a static factory method, or a builder API.

听起来 SomeClient 是从其他地方提供的(可能是像 Spring 的 DI),但你需要它在 MyExtension 中。假设这种情况,您可以从以下内容开始:

@ExtendWith(SpringExtension.class)
public class MyTestClass {
  @Autowired SomeClient someClient;
  @RegisterExtension
  MyExtension myExtension = new MyExtension(someClient);
}

实现此目的的一种方法是在上下文对象上使用 getTestInstance()

public class MyExtension implements BeforeEachCallback {
    @Override
    public void beforeEach(ExtensionContext context) throws Exception {

        context.getTestInstance().ifPresent(instance -> {
            if (instance instanceof SomeInterface) {
                SomeInterface some = (SomeInterface) instance;
                System.out.println(some.getSomeClient());
            }
        });
    }
}

这里可以看到两件事:

  1. 可能没有测试实例对象,例如在 BeforeAllCallback 中,因为通常每个测试都会创建测试实例。
  2. 需要演员表。这意味着您应该检查您的测试实例是否真的实现了 SomeInterface

话虽如此,我不太确定您为什么要走那条相当复杂的路线。 MyExtension 应该抽象出什么?