New to spring. I am trying to upgrade spring-boot-starter-parent from v1.3.2 to the latest v2.1.4. Made the POM file change for version upgrade. I have made all the necessary code changes to solve the compilation problems. when I am trying to run the test cases though, Test Classes which have #SpringBootTest annotation are failing with java.lang.IllegalStateException: Failed to load ApplicationContext
I have my application properties(application.properties) file in src/main/resources and the test application.properties file in src/text/resources
I have tried various annotations like
#ContextConfiguration()
#TestPropertySource(locations = "classpath:application.properties")
Still received the same error.
Below is the sample test class created.
package xxx.xx.xx.xxxx.controller
import org.junit.Assert;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = App.class)
#WebAppConfiguration
public class SampleTest {
//sample test case created to figure out
//test case runs successful when #SpringBootTest is not present
#Test
public void sampleTest1() {
int x= 2+3;
Assert.assertEquals(5, x);
}
}
I expect to able to run the test cases with that annotation.
Related
I have the following Configuration classes, one in the main package and one in the test package.
Main
#Configuration
public class DynamoConfiguration {
Test
#TestConfiguration
public class DynamoTestConfiguration {
Unit Test
#ActiveProfiles(profiles = "test")
#ContextConfiguration(classes = {DynamoTestConfiguration.class})
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest
public class DynamoClientTest {
Yet, it's still loading DynamoConfiguration and causing failures when I only want the DynamoTestConfiguration to be loaded. How can I ensure that happens?
When using #SpringBootTest, then your application is started, along with any #Configuration classes on the classpath. Spring has no idea that DynamoConfiguration is special and you don't want to load it.
As a way around this, you can use profiles:
#Profile("prod")
#Configuration
public class DynamoConfiguration {
and in your test, add !prod to your #ActiveProfiles:
#ActiveProfiles(profiles = "!prod,test")
#ContextConfiguration(classes = {DynamoTestConfiguration.class})
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest
public class DynamoClientTest {
This should avoid that DynamoConfiguration gets loaded in the test.
I got a multi module project where not every module is actually an application but a lot of them are libs. Those libs are doing the major work and I want to test them where they are implemented. The current dependencies of the libs:
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
In the main source is a class with #Configuration and a single bean:
#Bean public String testString() { return "A Test String"; }
I got 2 test classes:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles({"default", "test"})
public class Test1 {
#Test
public void conextLoaded() {
}
}
-
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles({"default", "test"})
public class Test2 {
#Autowired
private String testString;
#Test
public void conextLoaded() {
}
}
The first test works. The second does not. There is not #SpringBootApplication anywhere in that project so in the same package as the Tests I added a test configuration:
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan("com.to.config")
public class LibTestConfiguration {
}
And it does not work. Same for classes that are #Service. They are not in the context. How can I make it behave like a normal Spring boot application without it actually beeing one and load the configs and contexts from the configurations files I need? The default and test profile share most of their properties (for now) and I want them to be loaded like I would start a tomcat.
I switched to JUnit 5 and made it kinda work... So if you want to test Database stuff:
#DataMongoTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
class BasicMongoTest { ... }
Lets you autowire all repositories and mongo template
Initializes with apllicaton.yml config
Does NOT initialize or configure interceptors
Full application context test if you have a class with #SpringBootApplication in your classpath (Can be an empty test main in your test context)
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
public class FullContextTest { ... }
Initializes the full context with all configs and beans
Should not be done if not necessary as it loads all the application context and kinda defeats the purpose of unit tests to only activate whats needed.
Test only specific components and configs:
#SpringBootTest(classes = {Config1.class, Component1.class})
#EnableConfigurationProperties
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
public class SpecificComponentsTest { ... }
Initializes the context with only the Config1 and Component1 classes. Component1 and all beans in Config1 can be autowired.
I had an error during my jUnit test after adding a second test class.
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
...so I added (classes=...) part to the SpringBootTest annotation, and tests run fine.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {mySecondJUnitClass.class})
public class mySecondJUnitClass{
What I didn't understand is, I didn't add that "classes" to my first test class, I only set #SpringBootTest annotation and worked fine.
#RunWith(SpringRunner.class)
#SpringBootTest
public class myFirstJUnitClass{
When and why do we need that "classes" definition? Why is not #SpringBootTest not enough?
I found the answer (by the help of JB Nizet). My first test class was in the subpackage of the package of my main class and the second was not. So this was why it needed a classes definition.
I want to create integration test with docker before cucumber test start. Inspired by: http://tech.asimio.net/2016/08/04/Integration-Testing-using-Spring-Boot-Postgres-and-Docker.html
But in my case the TestExecutionListener is not started before database initialization. I use Flyway for database migrations and it seems it tries to initialize first. For this a database connection required, which is not available, due the TestExecutionListener not being executed.
These are my classes:
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {"json:target/integration-cucumber.json"},
features = "src/test/resources"
)
public class CucumberIntegration {
}
And my test class which I will be extended by the cucumber steps:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#SpringBootConfiguration
#ContextConfiguration
#TestPropertySource(locations="classpath:application.properties")
#TestExecutionListeners({
DockerizedTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class
})
public class SpringIntegrationTest {
}
When I change #SpringBootTest(classes = Application.class) to #SpringBootTest I see DockerizedTestExecutionListener being executed. Only it fails because it misses configuration from Application.class.
Anyone idea how to this with or without TestExecutionListener?
This will not work because the Cucumber.class JUnit runner doesn't support TestExecutionListeners.
You might think that your test steps are executed by the SpringRunner.class JUnit runner, but they aren't.
The #RunWith(SpringRunner.class) annotation on your SpringIntegrationTest class is basically useless here, since according to your post, your subclasses of SpringIntegrationTest contain Cucumber steps. However the SpringRunner expects JUnit #Test methods. Cucumber steps with #Given, #When, #Then etc. are ignored because SpringRunner doesn't know what to do with them.
What really executes your test steps is the #RunWith(Cucumber.class) annotation in your CucumberIntegration class. The Cucumber runner, however, doesn't give a damn about the TestExecutionListeners annotation that your steps definition class inherits from SpringIntegrationTest because it doesn't support such a feature.
Execution listeners are a Spring-only feature. You won't be able to do what you want to do, at least not as long as you use Cucumber.
I would like to create integration test in which Spring Boot will read a value from .properties file using #Value annotation.
But every time I'm running test my assertion fails because Spring is unable to read the value:
org.junit.ComparisonFailure:
Expected :works!
Actual :${test}
My test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebTests.ConfigurationClass.class, WebTests.ClassToTest.class})
public class WebTests {
#Configuration
#ActiveProfiles("test")
static class ConfigurationClass {}
#Component
static class ClassToTest{
#Value("${test}")
private String test;
}
#Autowired
private ClassToTest config;
#Test
public void testTransferService() {
Assert.assertEquals(config.test, "works!");
}
}
application-test.properties under src/main/resource package contains:
test=works!
What can be the reason of that behavior and how can I fix it?
Any help highly appreciated.
You should load the application-test.properties using #PropertySource or #TestPropertySource
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(locations="classpath:application-test.properties")
#ContextConfiguration(classes = {WebTests.ConfigurationClass.class, WebTests.ClassToTest.class})
public class WebTests {
}
for more info: Look into this Override default Spring-Boot application.properties settings in Junit Test
Besides the above marked correct answer, there is another nature way to load application-test.properties: Set your test run "profile" to "test".
Mark your test cases with:
#ActiveProfiles("test")
#RunWith(SpringJUnit4ClassRunner.class)
application-xxxx.properties is a naming convention for properties of different "profile".
This file application-xxxx.properties should be placed in src/main/resources folder.
"Profile" is also useful in bean configuration.