Replacing #SpringApplicationConfiguration by #SpringBootTest - java

I have a Spring Boot application with kinda lot unit tests. All the test classes are not annotated. They all just extend the following Abstract Class where they got their annotations from too:
#RunWith(SpringJUnit4ClassRunner.class)
#EnableAutoConfiguration
#SpringApplicationConfiguration(classes = { MoneyjinnConfiguration.class })
#WebAppConfiguration
#SqlGroup({ #Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = { "classpath:h2defaults.sql",
"classpath:testdata.sql" }) })
public abstract class AbstractTest {
}
Since Spring Boot 1.4.0 #SpringApplicationConfiguration is deprecated. I tried to replace it by #SpringBootTest and #ContextConfiguration(classes = { MoneyjinnConfiguration.class }) but now all my tests are failing with:
Caused by: java.lang.IllegalArgumentException: No auto-configuration attributes found. Is org.laladev.moneyjinn.businesslogic.service.impl.ContractpartnerAccountServiceTest annotated with EnableAutoConfiguration?
When I now annotate the concrete test-class with #EnableAutoConfiguration it continues to work again.
Honestly - I don't want to modify all my test classes by moving the #EnableAutoConfiguration from the abstract class to all concrete test classes. I kinda liked the approach of having all annotations in one central place - my AbstractTest class.
Am I missing something?

Related

How can I use #SpringBootTest(webEnvironment) with #DataJpaTest?

I am trying to test a JAX-RS application but I'd rather not mock the data especially since there's a buildData method for an existing #DataJpaTest
Here's what I am trying so far:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = MyApp.class
)
#DirtiesContext
#DataJpaTest
public class MyResourceTest {
I get the following error
java.lang.IllegalStateException: Configuration error: found multiple declarations of #BootstrapWith for test class [app.MyResourceTest]: [#org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.context.SpringBootTestContextBootstrapper), #org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper)]
The other ones I saw that are similar do not talk about the webEnvironment setting:
How do I import configuration classes in a #DataJpaTest in a SpringBootTest?
Recommended way to remove #DataJpaTest when used along with #SpringBootTest
There is somewhat of a solution using #AutoConfigureTestDatabase but when I did that only the first one works because buildData is annotated with #Before (same as in #DataJpaTest) as I want the data to be pristine before each test so I can do failure scenarios.
Switching to #BeforeClass also won't work because I won't be able to use the #Autowire Repository objects.
The #DataJpaTest documentation states the following:
If you are looking to load your full application configuration, but use an embedded database, you should consider #SpringBootTest combined with #AutoConfigureTestDatabase rather than this annotation.
Keep in mind that #DataJpaTest is annotated with #Transactional and #DirtiesContext. So you may need those annotations along with #AutoConfigureTestDatabase.
Actually when doing the answer in https://stackoverflow.com/a/57609911/242042, it solves the immediate problem, but you won't be able to do any tests that involve the database using a REST client as #Transactional will prevent the data from being saved for the client to get.
To get this to work, #Transactional should not be used. Instead DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD should be used instead. This dramatically slows down each test (as in 1 second to 10 seconds per test) but at least it works.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = MyApp.class
)
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
#AutoConfigureTestDatabase
#AutoConfigureWebTestClient
public class MyResourceTest {
#Autowired
private TestRestTemplate restTemplate;
...
}

Recommended way to remove #DataJpaTest when used along with #SpringBootTest

We have an application that relies on Spring Boot 2.0. We are in the process of migrating it to JDK11 from JDK8. This also enabled us to update Spring Boot from 2.0 to 2.1. After reading through the changelog, it appeared there was any major change that needed for us.
Now the problem lies in where some test classes are annotated with both #SpringBootTest and #DataJpaTest. As per this and as well as the documentation, we are not supposed to use both together and instead we changed #DataJpaTest to #AutoConfigureTestDatabase. Here is how the code is:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {A.class, B.class}, properties = {
"x=xxx",
"y=yyy"
})
#AutoConfigureTestDatabase // Used to be #DataJpaTest
#EnableJpaRepositories("com.test")
#EntityScan("com.test")
public class Test {
#TestConfiguration
public static class TestConfig {
// Some beans returning
}
// Tests
}
Now, we end up with the following error:
NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
So as per this answer, we did something like this:
#EnableJpaRepositories(basePackages="com.test", entityManagerFactoryRef="entityManagerFactory")
Even after this we still end up with the same error. Is this the right way to remove #DataJpaTest? Or do we need to remove #SpringBootTest and do something else? Any sort of guidance is much appreciated.
The testclass is annotated with #DataJpaTest and #ContextConfiguration
#RunWith(SpringRunner.class)
#DataJpaTest
#ContextConfiguration(locations = { "classpath:test-context.xml" })
public abstract class AbstractTestCase {
protected static final Logger LOG = LoggerFactory.getLogger(AbstractTestCase.class);
}
We defined a test-context.xml. This is because the testmodule is isolated from all other modules (multi maven module project). In the test-context.xml we defined the component-scan for the base-package.
<context:component-scan base-package="de.example.base.package" />

Ignore component scan when all tests are ignored

Is there a way to ignore component scan when all tests are ignored ?
Let's say I have test like this and all tests in class are ignored
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class})
#WebAppConfiguration
public class ReqMigration {
//.... all tests are ignored
}
TestConfig.class looks like this
#Configuration
#PropertySource(value = "file:${path.app}/configuration/env.properties", ignoreResourceNotFound=true)
#PropertySource(value= "classpath:migration-test.properties")
#Import({BpmConfig.class, MonitorConfig.class})
#ActiveProfiles({"monitor-elasticsearch"})
public class TestConfig {
...
}
The problem is when I push such a test and run build in CI (Teamcity) it still tries to scan everything defined in TestConfig.class even all tests are ignored. Can I override somehow this behaviour ?

What is the preferred way to configure a Spring Boot Test application?

I have configured my application using #Configuration annotated classes in the config package:
main
java
com.ourcompany
config
PersistenceConfig
JacksonConfig
persistence
...
Application
test
java
com.ourcompany
persistence
PersistenceTest
The configuration gets picked up by the Application class without a problem:
#SpringBootApplication
public class Application {
public static void main( String[] args ) {
SpringApplication.run( Application.class, args );
}
}
However, the Test class:
#RunWith(SpringRunner.class)
#DataMongoTest
public class PersistenceTest {
...
}
does not pick up the configuration, unless I specify the name of the configuration class in the annotation:
#SpringBootTest(classes = PersistenceConfig.class)
I find this quite unintuitive, since I have to explicitly list all my config classes.
What is the idiomatic way to share the configuration in Spring Boot?
EDIT
In the end the problem was with including both #DataMongoTest and #SpringBootTest at the same time. Removing the #DataMongoTest and annotating the test class as:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
public class PersistenceTest {
...
}
Solved the problem. However, the question as to what is the best practice remains.

How to combine many Spring test annotations in a single annotation?

I am using Spring Boot's convenient annotations on my test classes, for integration tests.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Config.class)
#IntegrationTest
#Sql({"classpath:rollback.sql", "classpath:create-tables.sql"})
#Transactional
I found it quite ugly to copy/paste this whole block on each test class, so I have created my own #MyIntegrationTest annotation
#SpringApplicationConfiguration(classes = Config.class)
#IntegrationTest
#Sql({"classpath:database-scripts/rollback.sql", "classpath:database-scripts/create-tables.sql", "classpath:database-scripts/insert-test-data.sql"})
#Transactional
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(value = RetentionPolicy.RUNTIME)
#interface MyIntegrationTest {
}
However, if I add the #RunWith(SpringJUnit4ClassRunner.class) in my new annotation, then JUnit will run with its default runner - which is not desirable.
So for now I have to use two annotations.
#RunWith(SpringJUnit4ClassRunner.class)
#MyIntegrationTest
I guess it is fine for now, but is there a way to combine these annotations, so I would be able to use a single annotation?
Meta-annotations are not the only way of code reuse. We use inheritance instead:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Config.class)
#IntegrationTest
#Sql({"classpath:rollback.sql", "classpath:create-tables.sql"})
#Transactional
public abstract class IntegrationTest {
}
public class FooTest extends IntegrationTest {
}
public class BarTest extends IntegrationTest {
}
Unlike meta-annotations, annotation inheritance from base classes is understood by both Spring and JUnit.
Ok so I've found some old discussions about this on the JUnit GitHub:
https://github.com/junit-team/junit/issues/194
https://github.com/junit-team/junit/issues/202
It is some sort of trade-off between readability and DRY-ness.
Allowing some meta-annotations could also slow down tools like IDEs.
I doesn't seem that it's on the way to be implemented any time soon, so for now I will have to keep my two annotations.

Categories