What is the difference between #CamelSpringTest and #CamelSpringBootTest? - spring

I am just starting a projects that combines Spring Boot and Apache Camel (what seems a very nice combination).
In regards of testing with JUnit 5 Google brings up this page. It suggests to use #CamelSpringTest if you want to have both the annotations from Spring and Camel (what i want).
At the and of the page there are suggestion for JUnit 4 Test migration that suggests:
Usage of #RunWith(CamelSpringRunner.class) should be replaced with
#CamelSpringTest
...
Usage of #RunWith(CamelSpringBootRunner.class) should be replaced with
#CamelSpringBootTest
My question is for new project/ tests: What is the difference of both in regards of usage.
There is of course difference in code but i don't have a clue for what reason:
CamelSpringTest:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE })
#ExtendWith(SpringExtension.class)
#BootstrapWith(CamelTestContextBootstrapper.class)
#TestExecutionListeners(
value = {
CamelSpringTestContextLoaderTestExecutionListener.class,
StopWatchTestExecutionListener.class
},
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public #interface CamelSpringTest {
}
CamelSpringBootTest
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE })
#ExtendWith(SpringExtension.class)
#TestExecutionListeners(
value = {
CamelSpringTestContextLoaderTestExecutionListener.class,
CamelSpringBootExecutionListener.class,
StopWatchTestExecutionListener.class
},
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public #interface CamelSpringBootTest {
}

The difference is quite simple: is the test running in a SpringBoot project or not?
Camel can be used in a pure Spring project (without Boot). In such a project you can either extend CamelSpringTestSupport to write Tests or you can use #CamelSpringTest annotation. The tests are then started and initialized for this environment.
When you use Camel in a SpringBoot project, Camel can leverage this environment with auto-configuration etc. Therefore you have to use another annotation, #CamelSpringBootTest, to write tests. See here for an example.
So the two annotations are just "activators" for two different Camel test facilities for different project types. And these are just the Spring facilities. See this page for even more Camel test facilities.
This is also the source of many confusions around Camel testing. There are so much test variations, Spring, SpringBoot, JUnit 4, JUnit 5 etc. it is really hard to know what annotations are needed for what combinations of testing setup.

Related

Spring Boot 2.1 - #WebMvcTest without Spring Security Auto-Configuration

Before migrating to Spring Boot 2.1, we had a couple of controller tests in our services utilizing #WebMvcTest in combination with #AutoConfigureMockMvc:
#WebMvcTest(SomeController.class)
#AutoConfigureMockMvc(secure = false)
public class SomeControllerTests { ... }
This had the effect that the Spring Security configuration was disabled and you could run MVC tests without mocking OAuth/JWT.
In Spring Boot 2.1, the secured attribute is deprecated and the release notes mention that
[...] #WebMvcTest looks for a WebSecurityConfigurer bean [...].
In order to avoid the deprecated secured attribute and loading of our WebSecurityConfigurer we rewrote our tests to:
#WebMvcTest(
value = SomeController.class,
excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebSecurityConfigurer.class),
excludeAutoConfiguration = MockMvcSecurityAutoConfiguration.class)
public class SomeControllerTests { ... }
The question is: is there a more compact way in Spring Boot 2.1 to define such tests?
Yes, rather than working around the fact the flag is deprecated, you should embrace the fact that this is going in that direction going forward.
As of Spring Boot 2.1, if you have Spring Security, your tests will be secured using your custom configuration. What is the actual problem with that?
If you don't want to authenticate for certain tests, just use Spring Security's test infrastructure and add #WithMockUser.
Encountered the same scenario and what helped was using the below annotations instead of #WebMvcTest. In this case, #WithMockUser did not help.
#WebAppConfiguration
#Import({MockMvcAutoConfiguration.class})
#EnableConfigurationProperties({ResourceProperties.class, WebMvcProperties.class})
Classes that existed in controllers / value of #WebMvcTest goes into value of #Import annotation.
Source: https://github.com/spring-projects/spring-boot/issues/14227#issuecomment-688824627

How to integration test auto configuration for a custom Spring Boot style starter library?

I am writing a library to provide some functionality that is shared between multiple different Spring Boot applications that I work with.
I would like to do something similar to the auto-configuration that is provided by the many Spring Boot starter libraries exist. That, or some other simple declarative way to integrate my library with the ApplicationContext of the apps using it.
I have found some resources explaining how auto configuration works. I can figure out the above problem.
However, I have not been able to find any good examples of how I can test as part of my library's test suite that it suitably integrates with a Spring Boot application. Ideally, I would start up a simple Spring Boot app written in the library's test directly just for the sake of testing, add the right annotation to it, and be able to assert that the correct beans are then configured.
I have tried creating a TestApplication class that does that and writing integration tests using the SpringBootTest annotation but the TestApplication was never started before my test started.
What can I do to start up a simple app like that solely for the purpose of testing my library? My tests are written with Spock and Spock-Spring in case that changes things versus other test frameworks.
I was able to make it work with the following test class:
#SpringBootTest
#ContextConfiguration(classes = TestApplication)
class DummyIntegrationSpec extends Specification {
#Autowired
DummyService dummyService
void 'dummy service should exist'() {
expect:
dummyService.getMessage() == DummyConfiguration.MESSAGE
}
}
and this TestApplication class at src/test/groovy/com/example/project/TestApplication.groovy
#SpringBootApplication(scanBasePackages = 'com.example.project.config')
#EnableAutoConfiguration
class TestApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(TestApplication)
}
static void main(String[] args) {
SpringApplication.run(TestApplication, args)
}
}
The two key changes I had to make in order for the TestApplication to start and load the correct context when I moved my TestApplication class from src/main to src/test were:
the TestApplication class needed to be added to the ContextConfiguration annotation
the package that my library's Java config files live in needed to be added to the SpringBootApplication scanBasePackages field
The library auto-configuration does follow a similar structure to the one mentioned in the link tom provided.
Your auto-configuration should be automatically picked while your main spring application/test is starting and all beans will be registered in your context. They will be available for auto-wiring and follow your conditions and init order.
As a summary, make sure you have an auto-configuration annotated by #Configuration class with an #Import that imports your #Configuration annotated configuration classes (inside of them you define beans with methods annotated with #Bean). Also make sure you created a spring.factories file that include your auto-configuration class and that you removed the spring boot maven plugin (for the packaging to be right).
Also, make sure your auto-configuration project is NOT annotated by things like #SpringBootApplication, #EnableAutoConfiguration, #ComponentScan or other spring boot annotations that need to be only in the main spring boot projects (There should be one of them in each stack).
Please also see the article below:
Spring boot is based on a lot of pre-made auto-configuration parent projects. You should already be familiar with spring boot starter projects.
You can easily create your own starter project by doing the following easy steps:
Create some #Configuration classes to define default beans. You should use external properties as much as possible to allow customization and try to use auto-configuration helper annotations like #AutoConfigureBefore, #AutoConfigureAfter, #ConditionalOnBean, #ConditionalOnMissingBean etc. You can find more detailed information on each annotation in the official documentation Condition annotations
Place an auto-configuration file/files that aggregates all of the #Configuration classes.
Create a file named spring.factories and place it in src/main/resources/META-INF.
In spring.factories, set org.springframework.boot.autoconfigure.EnableAutoConfiguration property with comma separated values of your #Configuration classes:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
Using this method you can create your own auto-configuration classes that will be picked by spring-boot. Spring-boot automatically scan all maven/gradle dependencies for a spring.factories file, if it finds one, it adds all #Configuration classes specified in it to its auto-configuration process.
Make sure your auto-configuration starter project does not contain spring boot maven plugin because it will package the project as an executable JAR and won't be loaded by the classpath as intended - spring boot will not be able to find your spring.factories and won't load your configuration

Spring-Boot module based integration testing

I have a multi-module Spring-Boot project.
I was wondering how I can set up integration testing just to test Spring Data JPA repositories? The following approach fails with this exception:
HV000183: Unable to load 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath.
Since this module does not depend on the web module, there is no web application that can be started.
#RunWith(SpringJUnit4ClassRunner.class)
#IntegrationTest
#SpringApplicationConfiguration(classes = TestConfiguration.class)
class CardInfoRepositoryIT {
#Autowired CardInfoRepository cardInfoRepository;
#Test
void testLoadData() {
assert cardInfoRepository.findAll().size() == 1
}
}
As Marten mentioned, #IntegrationTest should only be used when you need to test against the deployed Spring Boot application (e.g., deployed in an embedded Tomcat, Jetty, or Undertow container). So if your goal is to test your repository layer in isolation, you should not use #IntegrationTest.
On the other hand, if your tests require specific Spring Boot functionality (in contrast to standard Spring Framework functionality, semantics, and defaults), then you will in fact want to annotate your test class with #SpringApplicationConfiguration instead of #ContextConfiguration. The reason is that #SpringApplicationConfiguration preconfigures the SpringApplicationContextLoader which is specific to Spring Boot.
Furthermore, if you want your repository layer integration tests to run faster (i.e., without the full overhead of Spring Boot), you may choose to exclude configuration classes annotated with #EnableAutoConfiguration since that will auto-configure every candidate for auto-configuration found in the classpath. So, for example, if you just want to have Spring Boot auto-configure an embedded database and Spring Data JPA (with Hibernate as the JPA provider) along with entity scanning, you could compose your test configuration something like this:
#Configuration
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
#EntityScan(basePackageClasses = User.class)
#Import({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
public class TestRepositoryConfig {}
And then use that configuration in your test class like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestRepositoryConfig.class)
#Transactional
public class UserRepositoryTests { /* ... */ }
Regards,
Sam
p.s. You might find my answer to the following, related question useful as well: Disable security for unit tests with spring boot
I resolved this by having the following test config class.
#Configuration
#EnableAutoConfiguration
#ComponentScan
#PropertySource("classpath:core.properties")
class TestConfiguration {
}
core.properties is also used by the main application and it contains datasource information. #IntegrationTest annotation can be removed on the test class.
I also added the following to the module as dependencies:
testRuntime 'javax.el:javax.el-api:2.2.4'
testRuntime 'org.glassfish.web:javax.el:2.2.4'

#ActiveProfiles in meta annotation and on test class not working

I created a meta annotation #EmbeddedMongoDBUnitTest that activates two profiles to be used in spring based unit tests. The basic setup works:
#Documented
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#ActiveProfiles({"embeddedMongoDB", "embeddedMongoDBUnitTest"})
public #interface EmbeddedMongoDBUnitTest {
}
#RunWith(SpringJUnit4ClassRunner.class)
#EmbeddedMongoDBUnitTest
#ContextConfiguration(...)
public class WorkingTest {
...
}
Now when trying to activate another profile with another #ActiveProfiles annotation on the test class itself, the profiles in #EmbeddedMongoDBUnitTest aren't activated anymore:
#RunWith(SpringJUnit4ClassRunner.class)
#EmbeddedMongoDBUnitTest
#ActiveProfiles({"h2IntegrationTests"})
#ContextConfiguration(...)
public class NotWorkingTest {
...
}
Is there a reason why this is not working or is this a bug in the spring test code?
This is not a bug: this is by design.
The reason this does not work is that this form of configuration is simply not supported by Spring.
The algorithm that the Spring Framework uses when searching for an annotation stops once it has found the first occurrence of the sought annotation. Thus, in your example, the #ActiveProfiles annotation on NotWorkingTest effectively shadows the #ActiveProfiles annotation on your composed #EmbeddedMongoDBUnitTest annotation.
Please note that these are the general semantics for annotations in the core Spring Framework. In other words, the behavior you are experiencing is not specific to the spring-test module.
Having said that, profiles declared via #ActiveProfiles are in fact inherited within a test class hierarchy (unless you set the inheritProfiles flag to false). But don't confuse class hierarchies with annotation hierarchies: Java supports inheritance for interfaces and classes but not for annotations.
Hope this clarifies things!
Sam (component lead for the spring-test module)

Using Spring #ActiveProfile in integration tests

I am using #Profile Spring annotations to choose between embedded, standalone and container managed data sources. In order to choose 'embedded' my integration tests are annotated to activate the appropriate profile:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class, classes={TestConfigWrapper.class})
#ActiveProfiles({"EMBEDDED_DB"})
public class SomeIntegrationTest {
The problem is that I would like to move '#ActiveProfiles' into TestConfigWrapper, but doing this doesn't get picked up and the application context won't load any DataSources.
This means I have to annotate every integration test with an #ActiveProfile which effectively means it becomes integration test boiler-plate and could easily hamper future refactoring.
Is there a way I can do this using java config?
Per comment from Hippooom use an abstract class to configure tests:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={WebAppInitializer.class})
#ActiveProfiles({Profiles.EMBEDDED_DB})
public abstract class ProfiledIntegrationTest {
}

Resources