13
votes

I have a BaseTest class which consists of several tests. Each test shall be executed for EVERY profile I list.

I thought about using Parameterized values such as:

@RunWith(Parameterized.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// @ActiveProfiles("h2-test") // <-- how to iterate over this?
public abstract class BaseTest {

@Autowired
private TestRepository test;

// to be used with Parameterized/Spring
private TestContextManager testContextManager;

public BaseTest(String profile) {
   System.setProperty("spring.profiles.active", profile);
   // TODO what now?
}

@Parameterized.Parameters
public static Collection<Object[]> data() {
  Collection<Object[]> params = new ArrayList<>();
  params.add(new Object[] {"h2-test" });
  params.add(new Object[] {"mysql-test" });
  return params;
}

@Before 
public void setUp() throws Exception {
  this.testContextManager = new TestContextManager(getClass());
  this.testContextManager.prepareTestInstance(this);
  // maybe I can spinup Spring here with my profile?
}

@Test
public void testRepository() {
  Assert.assertTrue(test.exists("foo"))
}

How would I tell Spring to run each test with these different profiles? In fact, each profile will talk to different datasources (in-memory h2, external mysql, external oracle, ..) so my repository/datasource has to be reinitialized.


I know that I can specify @ActiveProfiles(...) and I can even extend from BaseTest and override the ActiveProfile annotation. Although this will work, I only show a portion of my test-suite. Lots of my test-classes extend from BaseTest and I don't want to create several different profile-stubs for each class. Currently working, but ugly solution:

  • BaseTest (@ActiveProfiles("mysql"))
    • FooClassMySQL(annotation from BaseTest)
      • FooClassH2(@ActiveProfiles("h2"))
    • BarClassMySQL(annotation from BaseTest)
      • BarClassH2(@ActiveProfiles("h2"))

Thanks

3
Why not running all tests with parameter, e.g. if you use Maven it could be mvn test -Dspring.profiles.active=test. I'm not sure if you can achieve it by this parameterized class, mostly because Spring will most probably boot up its context before it will start executing your test and you have to set up active profile before that.Szymon Stepniak
Thanks. Very nice solution I haven't thought about. This will definitely do if there is not an elegant way to deal with it in code! I think the only problem with this might be that only a few tests (in fact, all my repository/jpa tests), need to have different profiles, while others don't / need to access different configs.Frame91
Cool! I will add it as an answer if it works for you.Szymon Stepniak
It's not the answer I am currently looking for, but I'll definitely mark it correct if there is not an elegant code-related solution ;)Frame91
is there a code related solution almost 2 years on?Alun

3 Answers

4
votes

If you use Maven you can actually specify active profile from command line (or env variable if needed):

mvn clean test -Dspring.profiles.active=h2-test

The approach with parameterized test may not work in this case, because profile has to be specified before Spring boots up its context. In this case when you run parameterized integration test the context will be already booted up before test runner starts running your test. Also JUnit's parameterized tests were invented for other reasons (running unit tests with different data series).

EDIT: Also one more thing - when you decide to use @RunWith(Parameterized.class) you wont be able to use different runner. In many cases (if not all if it comes to integration testing) you want to specify different runner, like SpringRunner.class - with parameterized test you wont be able to do it.

4
votes

Spring profiles are not designed to work in this way.
In your case, each profile uses a specific datasource.
So each one requires a Spring Boot load to run tests with the expected datasource.

In fact, what you want to do is like making as many Maven build as Spring profiles that you want to test.

Besides, builds in local env should be as fast as possible.
Multiplying automated tests execution by DBMS implementation that requires a Spring Boot reload for each one will not help.

You should not need to specify @ActiveProfiles .

It looks rather like a task for a Continuous Integration tool where you could define a job that executes (sequentially or parallely) each Maven build by specifying a specific Spring Boot profile :

mvn clean test -Dspring.profiles.active=h2

mvn clean test -Dspring.profiles.active=mysql

etc...

You can also try to perform it in local by writing a script that performs the execution of the maven builds.
But as said, it will slowdown your local build and also complex it.

3
votes

For what it's worth:

My use case was to run a specific test class for multiple spring profiles, this is how I achieved it:

@SpringBootTest
abstract class BaseTest {
 @Test void doSomeTest() {... ARRANGE-ACT-ASSERT ...}
}

@ActiveProfiles("NextGen")
class NextGenTest extends BaseTest {}

@ActiveProfiles("Legacy")
class LegacyTest extends BaseTest {}