We have multiple test classes in our spring boot application. Some of the classes contain integration tests, some contain unit tests.
These means that if I (e.g. with maven) let all tests to be executed, it will run all tests in all classes.
What I like to achieve is that the integration tests are executed only, if a specific spring profile is set, e.g. via application.yml.
I like e.g. to annotate the whole test class to define that the tests in this class are only executed if the specified spring profile is set.
If it is not set, these tests shall be ignored.
The topic How can I use #IfProfileValue to test if a Profile is active? goes in exactly this direction. #IfProfileValue looks at first glance exactly like it is what I need.
But as it is pointed out, it is not. I could use it, if I would set a specific system property. But I need to use a real spring profile (and not the system property spring.profiles.active - this would ignore a profile set via application.yml)
#Profile seems to look also to be what I need but as the topic Use #Profile to decide to execute test class shows, we should not use it.
So what can be done to achieve this?
Note that there are a lot of questions about tests and spring profiles on stack overflow. But most of them point out how to set configurations in tests specific to spring profiles. That is not would I am looking for.
I would like to execute or ignore the tests.
I don't know exactly how you want to achieve it, but here is a way if you are using junit to conditionally ignore some tests at runtime simply using a configuration property:
application.properties:
test.enabled=true
then in your test code you can use org.junit.Assume and a property like the following:
#Value("${test.enabled}")
private Boolean testEnabled;
#Test
public void test {
org.junit.Assume.assumeTrue(testEnabled);
// your test code
}
now if you set the property test.enabled to true the test will run, otherwise it will be ignored.
Source: Conditionally ignoring tests in JUnit 4
Using JUnit 5, you can use an #Autowired Environment to check if a profile is active #BeforeEach test is run:
Assumptions.assumeTrue(Arrays.asList(this.environment.getActiveProfiles()).contains("integration"));
This checks for a profile named "integration" and works regardless of how the profile was set (system property, environment variable, application.yml, etc.).
If the profile is not active, the test will be ignored, which is similar to using the #Disabled annotation.
It is very easy. My solution in kotlin:
Create annotation
import org.springframework.test.context.junit.jupiter.EnabledIf
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.CLASS
import kotlin.annotation.AnnotationTarget.FUNCTION
#Target(CLASS, FUNCTION)
#Retention(RUNTIME)
#EnabledIf(
expression = "#{environment.acceptsProfiles('integration')}",
reason = "🏋🏻 Because spring.profiles.active = integration",
loadContext = true)
annotation class Integration
Use it:
import by.package.Integration
#Integration
internal class IntegrationTest {
#Test
// #Integration
fun test() {
assertEquals(4, 2 + 2)
}
#DisableIf annotation has opposite logic
Related
In my spring boot project, I am using MockMVC to test controller(web) layer. But I also have AOP(AspectJ) logic in my project, when I run unit test for controller with MockMVC, the test also triggers AOP code, how can I prevent AOP code to be triggered while running unit test for controller?
#Test
public void testMyControllerMethod() {
...
// myRequest hits an endpoint function of my controller, there is also AOP intercept the function call, how can I disable AOP to be triggered while running test?
mockMVC.perform(myRequest).andExpect(okStatus)
}
Question is in my code comment :)
I have checked this answer, I understand to use the if() expression, but I don't get TestMode.ACTIVE, there is no such thing in Spring boot. If someone could let me know how to check whether code is running unit test or not at runtime, I would know how to prevent AOP logic run as well.
What I meant in the other answer, as Simon already tried to explain to you, is something like this:
package de.scrum_master.app;
public class TestMode {
public static boolean ACTIVE = false;
}
But actually there I also listed a few other options such as environment variables and system properties. If I were you I would use one of those because in your Maven or Gradle build it would be very easy to set properties or environment variables via configuration. Your if() pointcut could access those variables.
Especially in the context of Spring there is an even simpler option: a test application configuration. Just provide a configuration without aspects to your tests. That way you can have different configurations for
production environment,
unit tests (no aspects),
integration tests (e.g. with aspects but different from unit test and production).
et cetera.
The advantage here is that you don't need any if() pointcuts or build any other knowledge about test/production environments into your aspects, which is quite ugly. My other answer only shows what you can do, it does not say it is the best solution.
I have a #SpringBootTest which is used to execute an integration test on the server. Depending on the configuration I want the server to behave differently. The configuration itself is read by beans (scope = singleton) deep inside my app logic and they read the property via #Value annotation.
How could I execute the same test with different configuration settings? I have tried to write different test classes and annotate them with #TestPropertySource(properties = XYZ). But it seems that this affects all other tests as well (due to the singleton scope?). It there a way to reset the properties after the test?
To respecify my problem: I want to configure my bean with a different #Value property during my tests and this value should only be valid throughout this specific test execution.
Thanks ahead for any pointers.
I have a webservice, which is connecting to the client of other webservice by using property from the config. As in any organization, we have different environments. For testing, I wanted to hit testing env instead of local. This is how I override the default property value only for integration test. By doing this, I can hit the test env instead of default local env.
#SpringBootTest(value = {"eureka.client.enabled=false", // Don't start Eureka
"com.somepackage.webservicename.client.serviceUrl = http://nodename.envname:26730"})
Hope this helps!
I guess am trying to get a corner case to work here. In my current project there are about 20 integration tests. One new integration test requires #EnableAsync to make the test work:
#RunWith(SpringRunner.class)
#EnableAsync
#SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class MyITest {
:
}
When run alone, this test works fine.
Considering Maven and Eclipse' execution of tests in one project and knowing that the environment is only created once and reused (or soft-reset) for all integration tests, it's somewhat a requirement that this integration test runs first. However, that's (nearly?) never the case.
Therefore, this integration test (nearly?) always fails. One obvious solution is to add #EnableAsync to all integration tests. However, that's a bad dependency which I bet is broken once somebody adds another integration test and forgets this requirement.
I'm looking for a way to force the SpringRunner to completely reset the context and really start it from scratch also looking at #EnableAsync. Ideally that way includes to flag that SpringRunner has to reset the context (i.e., remove the #EnableAsync) after the test, too. That way any order of execution would ensure that only that very one test has the #EnableAsync.
Is there a way to do this? Or can I manually turn on/off the async-stuff by code in a #Before/#After method?
take a look at DirtiesContext
Not sure if this is what you're looking for.
Possible duplicate of: How do you reset Spring JUnit application context after a test class dirties it?
Whow, I think I just found out by accident... What I have now:
#RunWith(SpringRunner.class)
#EnableAsync
#SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
ClassWithAnAutowiredAsyncDependency.class // <=== difference!!! ===>
})
public class MyITest {
:
#Autowired
private ClassWithAnAutowiredAsyncDependency mine;
:
}
It seems as if the given classes are reset (specially?) or at least the autowiring happens in there again or something. I can't explain it any different.
I'm sure that this integration test is not the first integration test being run and still the asynchronous bit seems to be in place.
Well, test is green, it works...
I am trying to run a spring JUnit test case using -
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:some.xml" })
The xml has bean defined along with in memory db details
<bean id="orderService" class="com.example.OrderServiceImpl">
<!-- set properties, etc. -->
</bean>
I am doing #Value injection inside the bean class OrderServiceImpl, but it does not happen while executing the test case but the same runs fine when I run the application. Can you please help ?
You need to add a PropertySourcesPlaceholderConfigurer or PropertyPlaceholderConfigurer to your test context. This SO question may give you a hint: Populating Spring #Value during Unit Test.
Check to see if a some.xml exists in both main and test trees. If it exists in both, the one in the test tree should override the one in the main branch.
Make sure the some.xml you are actually loading has a property-placeholder, such as
<context:property-placeholder location="classpath:some.properties"/>
I realize that there are more modern ways to manage properties, but this is simple and easy to specify for unit tests.
I find that multiple tests become very awkward with config files on the classpath, so I like to take advantage of a feature of the #ContextConfiguration that lets me create a dedicated minimal config for each test. The way it works is that for each test class, by convention, it can look for a config file in the same relative directory path as your test class package, and named after your test case. That way you can completely control the config and properties for each test case. You might try it--it can eliminate confusion caused by shared config files. To do it, remove the value in the#ContextConfiguration. Then, say you have a test case com.myCompany.SomeTest located in src/test/java/com/myCompany/. Create a file called SomeTest-context.xml in directory src/test/resources/com/myCompany and put the minimal config you need for that unit in the file. #ContextConfiguration will, by convention, find the config file of that name in that location and use it for your test. Although not part of the conventions I just spoke of, I put a properties file for each test in the same directory with just the properties I need for that test, named after the test case as well (e.g. SomeTest.properties). In your test case-specific context, add a property-placeholder line like this to get your test-specific properties:
<context:property-placeholder location="classpath:com/myCompany/SomeTest.properties"/>
At the top of your test case, you would put
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration // no parameters
public class FileEncryptionUtilsTest { ...
If you do that, you'll be able to inject beans and values to your heart's content, without worrying about side-effects of things being added to a shared context or dealing with errors arising from multiple context files with the same name.
The key advantage is that you are testing a unit here, not the integration represented by an application context file. If you share an application context file in your unit tests, you're testing the application context along with your bean, and that's more of an integration test goal, not a unit test need.
In my spring + maven app, I have created some tests for the Data Access Layer that I would like now to run against multiple datasources. I have something like:
#ContextConfiguration(locations={"file:src/test/resources/testAppConfigMysql.xml"})
public class TestFooDao extends AbstractTransactionalJUnit38SpringContextTests {
public void testFoo(){
...
}
}
It has currently the config location hardcoded, so it can be used only against one datasource.
What is the best way to invoke the test twice and pass two different configs (say testAppConfigMysql.xml and testMyConfigHsqlDb.xml)?
I've seen suggestions to do this via system properties. How can I tell maven to invoke the tests twice, with different values of a system property?
I don't know if there is some sexy and fancy solution, being simple as well, for this. I would just implement base class with all testing stuff and then inherit it into 2 classes with different annotation-based configuration, like this:
#ContextConfiguration(locations={"firstDs.xml"})
public class TestFooDaoUsingFirstDs extends TestFooDao {
}
#ContextConfiguration(locations={"secondDs.xml"})
public class TestFooDaoUsingSecondDs extends TestFooDao {
}
Unless you have to handle really high number of different datasources this way, that is OK for me.
Rather than file:..., you can use classpath:... (remove the src/test/resources, it's implicit if you use classpath). Then you can have a single master context with the line:
<import resource="dao-${datasource}.xml" />
If you run the Maven build with the option -Ddatasource=foo, it will replace the ${datasource} in the master context with the whatever you specify. So you can have datasource-foo.xml, datasource-bar.xml etc. for your different configurations.
(You need to enable Maven resource filtering in the POM for this to work).
Alternatively, check out the new stuff in Spring 3.1: http://www.baeldung.com/2012/03/12/project-configuration-with-spring/
Edit: A third option would be to have all the test classes extend some superclass, and use
Junit's #Parameterised, where the parameters are the different Spring contexts. You couldn't use #ContextConfiguration in that case, but you can always create the Spring context manually, then autowire the test class using org.springframework.beans.factory.config.AutowireCapableBeanFactory.autowireBean()
Check maven invoker plugin. It supports profiles also.