Reset Spring-Boot During Integration Tests - spring-boot

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...

Related

Prevent AOP(AspectJ) code to be triggered while running test

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.

Run integration tests only if specific spring profile is set

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

Executing extension before SpringExtension

I'm trying to implement integration testing in my app and have test class like that:
#ExtendWith(value={MyDockerExtension.class})
#ExtendWith(value={SpringExtension.class})
#WebAppConfiguration
#ContextConfiguration(classes={...})
#TestInstance(TestInstance.LifeCycle.PER_CLASS)
public class TestClass{ ... }
Is there any way to make MyDockerExtension execute some code, before whole SpringExtension start working and generate whole Context with Configurationc classes?
I've heard that order in which we declare extensions is the key, but sadly MyDockerExtension that implements BeforeAllCallback, AfterAllCallback executes right before test method and after whole context is loaded. In that situation it's to late to start containers with docker, becuase since whole context is loaded my app already tried to connect to the container.
At first I was skeptical about the order being fixed but you're correct:
Extensions registered declaratively via #ExtendWith will be executed in the order in which they are declared in the source code.
Regarding the MyDockerExtension, you may want to look at the extension point TestInstancePostProcessor, which is called before #BeforeAll. SpringExtension implements it and I guess it's there where it sets up the application context. If you also implement it, you should be able to act before it does.

Spring Batch Step Integration Testing

I'm looking for some general opinions and advice on testing a Spring batch step and step execution.
My basic step reads in from an api, processes into an entity object and then writes to a DB. I have tested the happy path, that the step completes successfully. What I now want to do is test the exception handling when data is missing at the processor stage. I could test the processor class in isolation, but I'd rather test the step as a whole to ensure the process failure is reflected correctly at step/job level.
I've read the spring batch testing guidelines and if I'm honest, I'm slightly lost within it. Is it possible to use StepScopeTestUtils.doInStepScope or updating the StepExecution to test this scenario? Ideally I'd force the reader to return faulty data before the processor kicks in.
Any advice would be greatly appreciated.
The best approach depends on the scope of your test. Reading a little between the lines here, I assume you are using a Spring IT, setting up a Spring context and using the JobLauncherTestUtils to start a job or a step.
I think the easiest way is replace one of your beans with a mock that triggers the error scenario. Using Mockito, this can be done by adding something like this to your test-configuration.
#Bean
public ReaderDataRepository dataApi(){
return mock(ReaderDataRepository.class);
}
This bean then overrides the actual implementation. In the test setup you can then configure this mock very explicitly.
#Autowired
private ReaderDataRepository mockedRepository;
#Before
public void setUp() {
when(mockedRepository.getData()).thenReturn(faultyData())
}
This involves very little manipulation of Spring 'magic' and very explicitly defines the error from within the test.

Integration testing against an http server - junit?

I want to to integration tests against an http server. So far I have only experiences with junit for unit testing.
I have two requirements: The framework must have a maven plugin and the tests cases code must be clean - so no dirty hacks and no boilerplate code.
Plain JUnit is good for unit testings, #Test methods are individual. But for integration testing I have to process several dependant steps which must exchange some kind of state (variables).
I already read:
Can we use JUNIT for Automated Integration Testing? and Passing JUnit data between tests and came to the conclusion that I don't like static fields in unit test and I don't want to use TestNG and add dependency annotations on tests and I don't want to put my test into one long unreadable test method.
I though more about some syntax like:
public class MyIntegrationTest() {
#Step
public void testCreate(Context context) {context.put("foo");}
#Step
public void testUpdate(Context context) {context.get();}
#Step
public void testDelete(Context context) {context.get()}
}
So I want to enhance/use ?Unit in a way that it executes #Step methods with a context instance as argument. The methods must be called by the framework in order and cannot be called individually. In a perfect world, all ?Unit guis would show the #Step like an #Test but this is optional...
Any hints how to do this?
Jan
The first point is to check the Maven Failsafe Plugin which is intended for doing integration tests with Maven. Second you have to name your Integration tests based on the conventions used by Maven FailSafe Plugin after that you should be able to run your integration tests simply with maven (by mvn clean verify).
So this means you have to name your integration test like MyIntegrationIT.java...To define the order of executions you have to use a different framework than JUnit may be TestNG which supports this kind of needs, but you already excluded it. So the questions is what kind of tests would you like to do? Page-flows etc. may be a look at JWebUnit might be look worth...
You might also want to consider http://httpunit.sourceforge.net/. It's useful for checking to see if responses come back from the http server.
However, it doesn't do the #Step functionality. Normally I'd do that by :
#Test
public void MasterTest() {
step1(..);
step2(..);
....
}
public void step1(...){...}
public void step2(...){...}

Resources