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.
Related
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;
...
}
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" />
Let's say I'm testing a repository :
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserRepositoryTest {
#Test
(...)
#Test
(...)
}
I'm ok that spring loads other repositories, but I'm not ok it loads the embedded Tomcat, the services, the controllers, ... every time I launch one of these JUnit.
What is the simplest way to achieve this?
I've tried to put some inner #Configuration class with a #ComponentScan limited to my repository package but it didn't work (it was just ignored).
Use the annotation #DataJpaTest instead of #SpringBootTest. It only loads the persistence related part of Spring.
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserRepositoryTest {
#Test
(...)
#Test
(...)
}
You will find a detailed solution here
If you have some usage of JdbcTemplate then take a look to this answer
It looks like there is not one single answer to this question.
Of course, for JPA repositories, Lore answer is the best : use #DataJpaTest (or #JdbcTest for my use case). But be also sure to use "#AutoConfigureTestDatabase(replace = Replace.NONE)" if your test data is in your database and not in some in-memory one.
Also there is a special chapter talking about this in Spring doc :
Spring Boot’s auto-configuration system works well for applications
but can sometimes be a little too much for tests. It often helps to
load only the parts of the configuration that are required to test a
“slice” of your application.
source : https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-testing-autoconfigured-tests
But it doesn't show all you can/need to do.
For example, I had a smtpClientService to test.
To test this service, alone in its own layer, I had to do these specific adaptations (if I omit "#AutoConfigureWebClient", I won't get RestTemplateBuilder injected) :
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureWebClient
public class smtpClientServiceTest {
#Autowired
SmtpClientService service;
#Configuration
#Import(SmtpClientConfig.class)
#ComponentScan(basePackageClasses = SmtpClientService.class)
static class TestConfiguration {
}
#Test
public void testSendMessage() {
(...)
}
}
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?
I have large number of test cases which runs with Spring Junit Support with following annotations on each Test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="classpath:spring/applicationContext.xml")
#TransactionConfiguration(transactionManager="transactionManager")
#Transactional
#ActiveProfiles("test")
Instead of putting all these annotations on each Test class I want to Create a Custom Annotation and use it.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="classpath:spring/applicationContext.xml")
#TransactionConfiguration(transactionManager="transactionManager")
#Transactional
#ActiveProfiles("test")
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface SpringJUnit4TestConfig {
}
But when I use this Custom Annotation Spring Injection is not happening at all.
#SpringJUnit4TestConfig
public class UserServiceTest
{
}
What I am missing here?
PS:
But JUnit's #RunWith and Spring's #Transactional, #ContextConfiguration all have #Inherited..So I thought it should work. But for now I get through it through a work around. Created a Based Abstract class and put all those Annotations on it and the test cases extending that Base class.
I don't think that this approach will work, as #RunWith is JUnit annotation, so for this to work JUnit annotations must be transitive.
When a class is annotated with #RunWith or extends a class annotated with #RunWith, JUnit will invoke the class it references to run the tests in that class instead of the runner built into JUnit. We added this feature late in development. While it seems powerful we expect the runner API to change as we learn how people really use it. Some of the classes that are currently internal will likely be refined and become public.
So test JUnit test invoking seem to work only for the inherited classes.
As for Spring, it supports a thing called context configuration inheritance for configuration locations, I'm quite not sure that this should work for other annotations types (please point me to the documentation, if you think otherwise) Actually, this will likely work for other annotations in case of test configuration class inheritance because #Transactional annotation itself is declared with #Inherited for this case.
So in this case, it does not run because of JUnit annotation's missing.