Novice User
Novice User

Reputation: 3824

Conditionally skip a Junit 5 test

In my Junit Jupiter API 5.5 test, I am calling my method which internally makes a HTTP call to a remote service. Now the remote service can be down or behave incorrectly. I want to skip my test in case the remote service is not behaving expectedly.

@Test
void testMe() {
  // do something
  Result res1 = myObject.retrieveResults(params)
  // assert something

  Result res2 = myObject.retrieveResults(param2)
  //asert on results
}

Result retrieveResults(Parameters param) {
 // do something
  // call to remote service
  // if they do not give result throw CustomException()
  // return Result
}

So basically in my test i would want to check if myObject.retrieveResult is throwing CustomException then skip that test, otherwise evaluate normally.

Upvotes: 8

Views: 13309

Answers (1)

Mansur
Mansur

Reputation: 1829

We have 2 different ways to accomplish this tasks in JUnit 5.

For demo purposes, I have created a basic class which sends a request to the url that is passed as an argument to its call(String url) method and returns true or false depending on the request result. The body of the method is irrelevant here.

Using Assumptions.assumeTrue()/assumeFalse() methods

Assumptions class provides us with two overloaded methods - assumeTrue and assumeFalse. The idea is that, if the assumption is wrong, the test will be skipped.

So, the test will be something like this.

@Test
void call1() {
    Assumptions.assumeTrue(new EndpointChecker(), "Endpoint is not available");
    Assertions.assertTrue(HttpCaller.call("https://www.google.com"));
}

Here is the code for EndpointChecker class.

static class EndpointChecker implements BooleanSupplier {
    @Override
    public boolean getAsBoolean() {
        // check the endpoint here and return either true or false
        return false;
    }
}

When the test is run, the availability of the endpoint will be checked first, if it is up, then the test will run.

Using JUnit 5 extension mechanisms.

So, let's start with creating the annotation. It is pretty straightforward.

@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EndpointAvailabilityCondition.class)
public @interface SkipWhenEndpointUnavailable {

    String uri();

}

And EndpointAvailabilityCondition class. Even though, it looks big, overall logic is very simple.

import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation;

public class EndpointAvailabilityCondition implements ExecutionCondition {

    @Override
    public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
        final var optional = findAnnotation(context.getElement(), SkipWhenEndpointUnavailable.class);
        if (optional.isPresent()) {
            final SkipWhenEndpointUnavailable annotation = optional.get();
            final String uri = annotation.uri();
            // check connection here start
            boolean result = false; // dummy value
            // check connection here end
            if (result) {
                return ConditionEvaluationResult.enabled("Connection is up");
            } else {
                return ConditionEvaluationResult.disabled("Connection is down");
            }
        }
        return ConditionEvaluationResult.enabled("No assumptions, moving on...");
    }

}

Hence, we can do the following in our tests.

@Test
@SkipWhenEndpointUnavailable(uri = "https://www.google.com")
void call2() {
    Assertions.assertTrue(HttpCaller.call("https://www.google.com"));
}

We can go ahead and add @Test annotation over @SkipWhenEndpointUnavailable and remove it from our test code. Like, so:

@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EndpointAvailabilityCondition.class)
@Test
public @interface SkipWhenEndpointUnavailable {

    String uri();

}
class HttpCallerTest {
    @SkipWhenEndpointUnavailable(uri = "https://www.google.com")
    void call2() {
        Assertions.assertTrue(HttpCaller.call("https://www.google.com"));
    }
}

I hope it helps.

Upvotes: 20

Related Questions