Is it possible to activate a spring profile in a WebMvcTest - spring-boot

Given a test class like:
#WebMvcTest
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.profiles.active=test")
public class MyControllerTest {
... some tests
}
I get the error:
java.lang.IllegalStateException: Configuration error: found multiple declarations of #BootstrapWith for test class [com.example.MyControllerTest]: [#org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper), #org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.context.SpringBootTestContextBootstrapper)]
The desired goal is that I'm just running a controller test and thus for test performance reasons do not want to set up the whole context - I just want the "web layer".
I can remove the #SpringBootTest(properties = "spring.profiles.active=test") line - however, now I've not activated the test profile, which may customise the web context in some way via properties, e.g. jackson customisations that will no longer be applied. Is there a way to get a "web layer" only test and still activate a spring profile?
My environment is java version "10.0.2" 2018-07-17, spring boot 1.5.16.RELEASE

To set active profile, you can use #ActiveProfiles, like this
#WebMvcTest
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
public class MyControllerTest {
then you can you application-test yml or properties in test resources.

Additionally, if you are getting active profile in programmatic way like this:
String environment = System.getProperty("spring.profiles.active");
Then you can set this property in the static block in your test class:
#WebMvcTest
public class MyControllerTest {
static {
System.setProperty("spring.profiles.active", "foo");
}
}

Related

How to inject values from application.yaml to Junit 5 Test case

I am using Junit5 to write unit test cases in java. I have few values that I have added in application.yaml but I am not able to retrieve them in my test file eg. ownerMasterList value is coming as null but it is present in application.yaml
#ExtendWith(MockitoExtension.class)
public class OwnerServiceTest {
#Value("${owner.master-list}")
private ownerMasterList;
#Test
void findAllOwners(){
---test detail
}
}
Using ConfigFileApplicationContextInitializer alone does not provide support for #Value("${…​}") injection. Its only job is to ensure that application.properties files are loaded into Spring’s Environment. For #Value support, you need to either additionally configure a PropertySourcesPlaceholderConfigurer or use #SpringBootTest, which auto-configures one for you.
From doc
#RunWith(SpringRunner.class)
#ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
public class OwnerServiceTest {...}
Please try providing property source
By reading your code, it seems that you are using JUnit5 (#ExtendWith(MockitoExtension.class)).
The correct extension to use to trigger the launch of a Spring application context is (#ExtendWith(SpringExtension.class)). If you are using Spring Boot, you can use the #SpringBootTest annotation which is itself annotated with #ExtendWith(SpringExtension.class).
One point to note though: you should not wire an application context when unit testing your code. You should rather test your class in isolation and inject the needed property (here ownerMasterList) through the constructor (see #BeforeEach JUnit 5 annotation):
class OwnerServiceTest {
private OwnerService ownerService;
#BeforeEach
void setUp() {
this.ownerService = new OwnerService(new ArrayList<>(...));
}
}

Spring boot - How to set an Application Property #Value only for a particular class

We use an application property to enable/disable a functionality.
#Component
#ConditionalOnProperty(
value = "spring.redis.cache.producer.enabled",
havingValue = "true")
public class MyEntityListener {
// Omitted
}
But we don't want to activate this in most part of our test base, so we are adding a TestPropertySource and try to get rid of it after class using #DirtiesContext
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource(properties = {"spring.redis.cache.producer.enabled=true", "spring.redis.cache.listener.enabled=true"})
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#DirtiesContext
public class AimaCacheControllerTest {
// Omitted
}
But the Spring Application context is not necessarily restarted after this class, breaking many consequent tests. Since mvn surefire plugin is running the tests in random order, it is not an option to do #DirtiesContext(classMode=BEFORE_CLASS) in the following tests - as we don't know which one that will be.
So the main issue here is to be able to inject an application property and make sure it is reset after the tests are over. How would we achieve that?
I feel like this should be quite common case but I couldn't find any working solution yet.

How to load mongo repository to spring test application context?

I have to implement some test for some spring application. I am using #SpringBootTest annotation in my test:
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyTest(){
//some tests...
}
It works fine, but i do not want to load all application context and and limit it by adding one or more nessecary configuration class. I done it with #ContextHierarchy:
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ContextHierarchy(ContextConfiguration(classes = [SomeCofigClass1::class, SomeConfigClass2::class]))
class MyTest(){
//some tests...
}
//for example
class SomeCofigClass1(){
#Bean
fun(someMongoRepository: SomeMongoRepository){ \\<-- Problem is here
return SomeService(someMongoRepository)
}
}
/**
* My repository.
*/
interface SomeMongoRepository : MongoRepository<Job, String> {}
Because of context is partially loaded i got a error:
No qualifying bean of type 'SomeMongoRepository' available:....
How can i load repository in test application context?
I already tried:
1) Added #AutoConfigureDataMongo. I got error java.lang.IllegalStateException: Unable to retrieve #EnableAutoConfiguration base packages
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ContextHierarchy(ContextConfiguration(classes = [SomeCofigClass1::class, SomeConfigClass2::class]))
#AutoConfigureDataMongo
2) Replaced #SpringBootTest by #DataMongoTest. I got error Unable to retrieve #EnableAutoConfiguration base packages
#DataMongoTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ContextHierarchy(ContextConfiguration(classes = [SomeCofigClass1::class, SomeConfigClass2::class]))
3) With #DataMongoTest replaced #ContextHierarchy by #Import. With #Import annotation it loads all application context. This is not suit for me.
#DataMongoTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#Import(SomeCofigClass1::class, SomeConfigClass2::class)
Add #RunWith(SpringRunner.class) to load Spring's ApplicationContext during the test.
Furthermore, classes can be selected by using the #SpringBootTest#classes attribute (or #ContextConfiguration):
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {SomeCofigClass1.class, SomeConfigClass2.class})
If your test makes use of inner Configuration classes, be sure to make them static (and add #Configuration). From the documentation:
If you omit the classes attribute from the #ContextConfiguration annotation, the TestContext framework tries to detect the presence of
default configuration classes. Specifically,
AnnotationConfigContextLoader and AnnotationConfigWebContextLoader
detect all static nested classes of the test class that meet the
requirements for configuration class implementations, as specified in
the #Configuration javadoc.

How do I change a Spring property file variable in code in a Junit test?

I've got a
thing=false
In my property file for a Spring Boot project. While running my Junit tests for the project, I want
thing=true
How do I do this?
Just add application.properties file in:
src -> test -> resources
and set property to desired value.
You can set a particular configuration property for your unit tests using the TestPropertySource annotation with the properties parameter:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
#TestPropertySource(properties = {
"thing=true"
})
public class ThingTrueTests {
}
This provides a way to set a configuration property without using a properties/yaml file. However it is a class-level annotation so it will apply to all the tests in that class.
If you have a set of tests that rely on thing being false then put these in one unit test class and put all the tests that rely on thing being true in another unit test class.
Note: If you're writing groovy code then the TestPropertySource annotation would look like this:
#TestPropertySource(properties = [
"thing=true"
])
Spring provides a support for the same use #TestPropertySource annotation , is a class-level annotation that is used to configure the locations() of properties files and inlined properties() to be added to the Environment's set of PropertySources for an ApplicationContext for integration tests.
Example
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = AppConfig.class)
#TestPropertySource(locations="classpath:test.properties")
public class SampleApplicationTests {
}
in your test.properties present in Junit class path you can override the variable you want mentioned under application.properties

Prevent Application / CommandLineRunner classes from executing during JUnit testing

If in your TestCase class there is this annotations:
#SpringApplicationConfiguration(classes = {Application.class})
this will cause the Application.class, implementing the CommandLineRunner interface, to run the required method
public void run(String... args) throws Exception
I still think this is, mostly, a not wanted behaviour, since in your test environment you may not want to launch the entire application.
I have in mind two solution to circumvent this problem:
to remove the CommandLineRunner interface from my Application class
to have a different context for testing
Both this solution requires lot of coding.
Do you have a more convenient solution?
Jan's solution can be achieved easier.
In your test class, activate the "test" profile:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
public class MyFancyTest {}
In your CommandLineRunner set the profile to NOT test:
#Component
#Profile("!test")
public class JobCommandLineRunner implements CommandLineRunner {}
Then you don't have to manually set the profile in the Application.
As mentioned in the spring documentation you can use #ContextConfiguration with a special initializer:
ConfigDataApplicationContextInitializer is an ApplicationContextInitializer that you can apply to your tests to load Spring Boot application.properties files. You can use it when you do not need the full set of features provided by #SpringBootTest
In this example anyComponent is initialized and properties are injected, but run(args) methods won't be executed. (Application.class is my main spring entry point)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class,
initializers = ConfigDataApplicationContextInitializer.class)
public class ExtractorTest {
#Autowired
AnyComponent anyComponent;
#Test
public void testAnyComponent() {
anyComponent.anyMethod(anyArgument);
}
}
You can define a test configuration in the same package as your application that looks exactly the same, except that it excludes beans implementing CommandLineRunner. The key here is #ComponentScan.excludeFilters:
#Configuration
#ComponentScan(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = CommandLineRunner.class))
#EnableAutoConfiguration
public class TestApplicationConfiguration {
}
Then, just replace the configuration on your test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = TestApplicationConfiguration.class)
public class SomeApplicationTest {
...
}
No CommandLineRunner will be executed now, because they are not part of the configuration.
I'm a bit late to the party, but a reasonable approach is to mark the bean with #ConditionalOnProperty, e.g.
#ConditionalOnProperty(prefix = "job.autorun", name = "enabled", havingValue = "true", matchIfMissing = true)
public CommandLineRunner myRunner() {...}
The following annotation will then disable it in tests:
#SpringBootTest(properties = {"job.autorun.enabled=false"})
If you have a mocking framework installed (e.g. MockMVC) you can create a mock instance of the CommandLineRunner implementation, more or less disabling it:
#MockBean
private TextProcessor myProcessor;
Previous answers didn't work wor me. I ended up using different profiles - example for the init method in Spring Boot:
SpringApplication app = new SpringApplication(AppConfig.class);
app.setAdditionalProfiles("production");
app.run(args);
This is not executed during the tests so we're safe here.
All tests have their own profile "test" (which is useful in many other ways, too):
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
public class MyFancyTest {}
The command-line runner is annotated with the "production" profile so the tests ignore it:
#Component
#Profile("production")
public class JobCommandLineRunner implements CommandLineRunner {}
I solve this by not implementing CommandLineRunner. Just get a bean from the context, and call a method on it, passing argv. That way you will get the same result, and the application won't start automatically when running the tests.

Resources