0

Note: if you have never seen test engines, you might be very confused at the beginning (at least it was for me). There is an example here.


In JUnit5 it is possible to create a custom TestEngine that can discover and execute tests (not only the standard @Test-annotated methods). The documentation for this extension is here, but this post is definitely more exhaustive. There are very few resources and demo on this advanced topic, so I tried to experiment a little. For what I have understood, there are basically 2 approaches:

  • implementing both discover and execute methods of the TestEngine interface
  • extending from HierarchicalTestEngine and implementing only the discover method

I would say the second one is the cleanest, even if I am not sure you have the same freedom during the execution phase.

In the first approach setting the TestExecutionResult for a test container is very easy

public void execute(ExecutionRequest request) {
    TestDescriptor rootDescriptor = request.getRootTestDescriptor();
    var listener = request.getEngineExecutionListener();
    ...
    listener.executionFinished(containerDescriptor, TestExecutionResult.successful());
}

The entire code is not important in this case, containerDescriptor is a TestDescriptor whose getType() returns Type.CONTAINER. In the real code I set the TestExecutionResult based on the results of the tests the container contains.


In the second approach how can I set the test execution result for a container? Afterall, only TestDescriptors with getType() == Type.Test are actually executed, therefore I can easily manipulate the results (overriding the Node.execute method). I look at the Node interface to see if there is something useful but I missed it. Even nodeFinished is not enough since I just receive the TestDescriptor of the container, not the test execution results of all the children.

Currently, this is the definition of my descriptor:

public class MyTestClassDescriptor extends AbstractTestDescriptor
        implements Node<MyEngineExecutionContext> {
  public MyTestClassDescriptor(Class<?> testClass, TestDescriptor parent) {
      super(...);
      ...
  }


  @Override
  public Type getType() {
    return Type.CONTAINER;
  }

}

I can certainly adapt the MyEngineExecutionContext to store a sort of map with all results, but it seems like reinventing the wheel.

Bonus question: when is the TestExecutionResult for a container meaningful?

Marco Luzzara
  • 5,540
  • 3
  • 16
  • 42
  • 1
    To answer „Bonus question: when is the TestExecutionResult for a container meaningful?“: A container can do any amount of setup, the failing of which would fail the container. Moreover, a test element could be both a test and a container depending on your model‘s programming model. – johanneslink Jul 01 '21 at 15:42
  • @johanneslink Thank you for the "bonus answer". If I have understood correctly, in this case the first reason you have given is perfectly valid, if my descriptor would be a `Type.CONTAINER_AND_TEST` the second one would apply as well. I had already read about the `Type.CONTAINER_AND_TEST`, but I have never seen it used in `junit-jupiter` or `jqwik`, do you have an example of use case? Sorry for the off-topic but I was lucky to gain your attention (as you have worked with test engines definitely more than I did), now that you have mentioned this possibility I would like to deepen it. – Marco Luzzara Jul 01 '21 at 16:56
  • I never checked the implementation but test methods in Spock COULD make sense as test and container since they have individual parts (when, then) that could be reported as failed or succeeded. In jqwik I started out to model properties as test and containers but then decided that the overhead of reporting each try as failed/succeded is not worth it. – johanneslink Jul 02 '21 at 05:22
  • Got it, thanks again. – Marco Luzzara Jul 02 '21 at 06:59

0 Answers0