Andrew Cheong
Andrew Cheong

Reputation: 30283

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

I would like to write a JUnit5 Extension that extends my test class,

@ExtendWith(MyExtension.class)
public class MyTestClass {

  @Test myTest1() {}

  @Test myTest2() {}

  // ...
}

However, my test class also implements a certain interface, so it looks more like this:

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

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

  @Test myTest1() {}

  @Test myTest2() {}

  // ...

  SomeClient getSomeClient() {
    // ...
  }

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

No mysteries so far.

But now, I want those interface implementations to be available to the extension as well, e.g.

public class MyExtension implements BeforeEachCallback, SomeInterface
{

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

How can I set up my classes to achieve this? (Or, what is the inherent flaw or code smell against doing this?)

Upvotes: 0

Views: 964

Answers (2)

drekbour
drekbour

Reputation: 3091

You need to use the @RegisterExtension annotation which allows you to construct your extension instance manually.

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.

It sounds like SomeClient is provided from elsewhere (a DI like Spring perhaps) but you need it in MyExtension. Assuming this scenario, you can start with something like:

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

Upvotes: 1

johanneslink
johanneslink

Reputation: 5351

One way to achieve that is to use getTestInstance() on the context object:

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());
            }
        });
    }
}

What you can see here is two things:

  1. There might not be a test instance object, e.g. in a BeforeAllCallback because test instances are usually created per test.
  2. A cast is required. That means you should check if your test instance really does implement SomeInterface

Having said that, I'm not really sure why you'd want to go down that rather complicated route. What's MyExtension supposed to abstract from?

Upvotes: 1

Related Questions