19

I have a spring-boot application for which I am writing IT tests.

The data for the tests comes from application-dev.properties when I activate dev profile

Here is what I have for tests:

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class ApplicationTests {

    @Autowired
    Environment env;

    @Test
    public void contextLoads() {
        System.out.println(Arrays.toString((env.getActiveProfiles())));

    }

}

ServiceITTest

public class ServiceITTest extends ApplicationTests {


     @value
     String username;

     @value
     String address;

     @Autowired
     MyService myService;


      @Test
      public void check_for_valid_username_address(){
            myService.validate(username,address);
      }
}

I want the above test to run only when I set the profile of "dev","qa". by default, it should not run.

Is it possible to get that fine control in spring boot testing?

brain storm
  • 30,124
  • 69
  • 225
  • 393

7 Answers7

12

You would want to use the @IfProfileValue annotation. Unfortunately it doesn't work directly on the active profiles but it can read a property so if you only define a specific property within the profiles that you want to run the test on then you can use that annotation on that specific property.

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#integration-testing-annotations-junit

Shawn Clark
  • 3,330
  • 2
  • 18
  • 30
  • 2
    An example of your suggested workaround would be very helpful. I can't figure this out. – Alex Worden Apr 30 '18 at 21:32
  • Direct pointer - https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#integration-testing-annotations-junit4-ifprofilevalue – Zon May 13 '21 at 07:56
7

It works also with active profiles - there is a property value containing active profiles:

Test only active with specific profile:

@IfProfileValue(name = "spring.profiles.active", values = {"specific"})

Since i have tests that should NOT run if specific profile is active i added this to those tests:

@ActiveProfiles(profiles = {"default"})

It does not work with @IfProfileValue and "default" and i also didn't found any "run if specific profile is not active.

dermoritz
  • 12,519
  • 25
  • 97
  • 185
  • I am not sure how you could get any upvotes for the answer. `@Active` profiles is activating a profile for a test and not disabling one like requested. `@IfProfileValue` neither solves the problem. This is not an answer to the question. – zyexal Jan 20 '21 at 08:47
  • "@IfProfileValue(name = "spring.profiles.active", values = {"specific"})" enables a test for given profile: tests with this will not be run for default profile. Tests annotated with "@ActiveProfiles(profiles = {"default"})" won't run on "specific" profile. this way you can turn on and of tests by profile - and this is the question about. – dermoritz Jan 20 '21 at 12:27
  • PO asks for "profile of "dev","qa". by default, it should not run." - setting any profile using @ActiveProfile("whatever") will not disable a test for "dev" nor "qa". `@IfProfileValue(name = "spring.profiles.active", values = {"specific"})` might work on method level (actually I don'tknow), but not on Class level, as the context isn't ready in the time of evaluation and `spring.profiles.active` will be `null`. – zyexal Jan 20 '21 at 13:53
  • sorry you are the only one that can not map. just rename my "specific" to "dev" or "qa". just change to values={"dev","qa"}. And if there are tests that you do not want to run if dev or qa is active use 2nd annotation. – dermoritz Jan 25 '21 at 07:52
5

In Spring you can also use the @DisabledIf annotation. It allows for specifying a Spring Expression Language expression. See this blog post for examples.

JUnit 5 also has:

  • @DisabledIfEnvironmentVariable
  • @DisabledIfSystemProperty
Mark Lagendijk
  • 6,247
  • 2
  • 36
  • 24
  • 1
    Can you please let us know, how exactly this shall work? In my case `@DisabledIf(expression = "#{environment['spring.profiles.active'] == 'foo-bar'}", loadContext = true)` did not worked. There is also an open question: [Using @EnabledIf with spring.profiles.active property in Spring Environment within tests](https://stackoverflow.com/questions/55705825/using-enabledif-with-spring-profiles-active-property-in-spring-environment-with) – zyexal Jan 20 '21 at 09:07
2

I wanted to exclude tests that required an external service but I couldn't get that to work the way I wanted (more or less a non-existent @IfNotProfileValue).

As an alternative, I used the JUnit Assume.assumeThat which provided the behavior I wanted. e.g.,

Assume.assumeThat("Skipping Test: No username property", this.username, not(isEmptyString()));

I ended up not using a profile to drive it but you should be able to define a property or use the Environment to determine if a profile is active.

The assumeThat can be used in @Test and @Before methods but be aware that the @After methods will still run so cleanup might need a code guard.

Michael Lihs
  • 7,460
  • 17
  • 52
  • 85
Nathan Niesen
  • 375
  • 4
  • 11
0

I had to do this recently.

import org.springframework.core.env.Environment;
import static org.junit.Assume.assumeFalse;

@Test
public void testSomethingWhenProfileIsNot(){
  assumeFalse("Ignore this test when the active profile is 'myprofile'",
     Arrays.stream(environment.getActiveProfiles()).anyMatch( profileName -> "myprofile".equals(profileName)));

}


@Test
@IfProfileValue(name = "spring.profiles.active", value = "myprofile")
public void testSomethingWhenProfileIs(){
  
}
Laurent Picquet
  • 1,147
  • 11
  • 12
0

I had to do something similar, disable / enable @Test methods using a Spring properties YAML file as different environments had different features which was turned off / on.

The solution was to create custom annotations e.g. FeatureA and FeatureB e.g.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf(value = "${ignore-feature.billing}", loadContext = true)
public @interface FeatureBilling {

}

... and ...

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf(value = "${ignore-feature.webhooks}", loadContext = true)
public @interface FeatureWebhooks {

}

The test methods could be annotated as follows: -

@SpringBootTest
public class TestScenarios {

    @Test                         
    public void test1() {
        // always run      
    }

    @Test
    @FeatureBilling         // method tests billing functionality
    public void test2() {

    }

    @Test
    @FeatureWebhooks        // method tests webhooks 
    public void test3() {

    }

    @Test
    @FeatureWebhooks        // method also tests webhooks
    public void test4() {

    }

    ...

}

So the application.yaml file can contain e.g.

ignore-feature:
  billing: false
  webhooks: false
  ... 
  

This means specific environments can be very granular about which features we test or not e.g.

application-staging.yaml could override these and disable where applicable: -

ignore-feature:
  billing: true
bobmarksie
  • 3,282
  • 1
  • 41
  • 54
-1

You can get the active profiles using org.springframework.core.env.Environment: (Beware, it's Kotlin.)

@Autowired
private val environment: Environment? = null

private fun isProfileActive(name: String) = environment!!.activeProfiles.contains(name)

@Test fun onlyOnOpenShift {
     org.junit.Assume.assumeTrue(isProfileActive("openshift"));
     ...
}

If you have a lot of cases where you would use it (which I would suggest might hint that you're doing something wrongly), then it may pay off to decorate the test method with an annotation like @OnlyIfProfileActive("openshift") and process it with your own JUnit extension, like, implementing org.junit.jupiter.api.extension.BeforeTestExecutionCallback and determine if the method should run. In such case, the environment can be obtained from the Spring test runner.

@Test @OnlyIfProfileActive("openshift")
fun onlyOnOpenShift {
     ...
}

Which, I suspect, is what annotations like @IfProfileValue do. But here you would have more control.

For instance, to make it versatile, it could be an expression evaluated with JEXL or JUEL or such.

@Test @SpringProfilesCondition("openshift && !mockDatabase")
fun onlyOnOpenShift {
     ...
}
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277