I'm trying to write an integration test for a spring boot project. Unfortunately, I'm confused with the implementation.
Below sample code snippet tried
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MainApplication.class)
#AutoConfigureMockMvc
public class PropertyControllerIT {
#Autowired
private MockMvc mockMvc;
#Test
public void sample_test() throws Exception {
this.mockMvc.perform(post("/property")).andExpect(status().is2xxSuccessful());
}
}
Question
Do I need to have a separate MainClass with
#SpringBootApplication annotated to support the integration test?
Do we create a mock database or override the database configuration. If yes, how do we override the configuration
Should we maintain a separate directory for integration-test similar to java or test like integration-test
You don't need to create any other classes annotated with
SpringBootApplication.
It's a good practice to run integration tests using in memory database for example H2. You can create a new application-test.properties file in resources folder inside of test directory. In order to use override properties you can run test with test profile. In order to run tests with test profile #ActiveProfiles can be used.
You can keep your integration tests in the same test directory like unit test but they can be located in a separate package
Related
I write integration test for SpringBoot application with opportunity to run multiple tests simultaneously they have file system dependencies that's why I need to create unique root folder for each integration test.
I have a spring bean in production app that have #PostConstruct section to perform long-running operations. These long-running operations rely on file system structure that I prepare in #Before section of unit-tests.
Unique folders for simultaneously running tests are set via root.directory=target/results/#{T(java.util.UUID).randomUUID().toString()}. This property is injected as #Value in spring #Component class to avoid re-calculation root directory in different places.
The main issue is following: I need to prepare folders which name should be specified via application.properties with some resources(copy files, folders) in #Before test-section and run #PostConstruct only after all resource are prepared.
I tried several variants: 1) autowire in the test a bean with #PostConstruct and invoke it programmatically in the end of #Before - it's a single working case and it looks strange and fragile
2) Replace #PostConstruct with InitializingBean and afterPropertiesSet - it doesn't work. Because I have a value for folder name on bean initialization stage but without copied resources that I copy in #Before test section
I hope I explained well. I will be appreciate with any help or advice.
Your Question are bit vague but to answer the issues.
The main issue is following: I need to prepare folders which name should be specified via application.properties with some resources(copy files, folders) in #Before test-section and run #PostConstruct only after all resource are prepared.
Make Sure you are running Test
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "myFileName=test.txt")
In test #Before use
#Rule
public final TemporaryFolder temporaryFolder = new TemporaryFolder();
temporayFolder.newFile( use #Value here)
So #Before will run before any spring boot startup and then call your function in one of the unit test methods
Now that Alfresco integration tests of custom modules are run using Docker, I wonder how to make additional Spring beans available in this context and how to access existing Spring beans in test classes.
Until Alfresco 5.x, I used to annotate the test class with
#ContextConfiguration("classpath:alfresco/application-context.xml")
This made the Spring context available. To make Spring beans from this context available in the test class, I annotated members like this:
#Autowired
#Qualifier("authenticationComponent")
private AuthenticationComponent authenticationComponent;
In addition I was able to define additional Spring beans in src/test/resources/alfresco/extension/test-context.xml.
Is this the approach to use when writing integration tests for 6.x and Docker?
At least the annotation org.springframework.test.context.ContextConfiguration is no longer included in a module build using the Maven 4.0.0 SDK archetype.
This blog post talks about the above mentioned annotations. But the dependencies pulled in by the pom.xml created from the SDK 4 archetype don't include these annotations.
A different approach seems to be to only use
#RunWith(value = AlfrescoTestRunner.class)
on the integration test class. But how do I get the Spring beans like nodeService injected into it? And how do I declare and make available additional Spring beans which are part of my custom module and required by the integration test to succeed?
You can get the Spring context via AlfrescoTestRunner as follows:
#Before
public void setUp() {
this.nodeService = (NodeService) super.getApplicationContext().getBean("nodeService");
}
I do the same with custom beans:
super.getApplicationContext().getBean(MyType.class);
Since the integration tests run in the repository, all of the Spring context is automatically available.
Note that your test class needs to extend AbstractAlfrescoIT for this to work.
An example class may look like this:
package nl.open.mystuff;
import org.alfresco.rad.test.AbstractAlfrescoIT;
import org.alfresco.rad.test.AlfrescoTestRunner;
import org.alfresco.service.cmr.repository.NodeService;
#RunWith(value = AlfrescoTestRunner.class)
public class MyCustomIT extends AbstractAlfrescoIT {
private NodeService nodeService;
private MyType myType;
#Before
public void setUp() {
this.nodeService = (NodeService) super.getApplicationContext().getBean("NodeService");
this.myType = super.getApplicationContext().getBean(MyType.class);
}
}
In Alfresco SDK 3, you can even add your own Spring XML files under src/test/resources/alfresco/extension/*-context.xml. I imagine this still works, but I haven't tried it with SDK 4 myself.
I develop web app with Spring Boot. I have problem with unit test for web layer.
For these tests I'm using annotation #WebMvcTest. Problem is that my main configuration class contains #PropertySource with java arg, which contains path to external properties file, and when I start my unit web test, error is occured that this java arg can't be parsed(of course I can add this java arg to run configuration of my test, but my web unit tests don't need this file).
My main config class:
#SpringBootApplication
#PropertySource(value = {"${pathto.configfile}"})
#EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
My first solution was to create separate configuration class with "!test" profile, and relocate #PropertySource("..") to it. And to my unit test add #ActiveProfiles("test")
My second Configuration class:
#Configuration
#Profile({"!test"})
#PropertySource(value = {"${pathto.configfile}"})
public class PropertyConfiguration {
}
For unit web test this solution works very good. But new problem appears in starting of my web app. In my external properties file I have
property spring.profiles.active. To this property I assign or db or file value. Depending on its value, apropriate implementation of Repository is created and injected to my service layer. When value is db, app starts good,
but when value is file error is being thrown: NoSuchBeanDefinitionException.
When I come back to my previous version(without second configuration file), app starts good in both cases(but not web unit tests)
So, explain please, why when value of spring.profiles.active=db, app starts good, but when spring.profiles.active=file- failed.And how I can solve my task?
I attempted to find how I can add other application context to my web unit tests, but I didn't find.
Any idea?:)
For my database repositories I'm using Spring Data JPA, so I don't create implementation of these repositories, but I create implementations of my file
repositories, and my implementations had #Profile("file"). After deleting this annotation from implementations, it leaved only on interfaces. I don't know why one config class worked, but two classes didn't. But problem is solved)
I am currently using Spring 4.0.6.RELEASE and have the following Spring configuration file:
#Configuration
#PropertySource({"classpath:config.properties"})
public class MyServiceConfig {
...
I was wondering if there is a way to run integration tests for my component-annotated-classes with a different properties file (let's say test-config.properties) in order to give different values for my value and autowired annotated properties and methods.
NOTE: I know that Spring 4.1.x comes with #TestPropertySource which helps to achieve it. But upgrading Spring to later versions is not an option.
Yes. Specify "profile" for integration tests.
#Configuration
#PropertySource({"classpath:test-config.properties"})
#Profile("integration-test")
public class MyServiceTestConfig {
...
In order to use this profile when testing repository use #ActiveProfiles annotation
#ActiveProfiles("integration-test")
#RunWith(SpringJUnit4ClassRunner.class)
public class MyRepositoryTest {
...
My test case looks something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class AbstractDatabaseTestCase extends AbstractTestCase {
#Autowired
private SessionFactory sessionFactory;
}
For some reason when I run it locally on my computer either through Eclipse (using Junit4 test runner) or using ant (both in eclipse, or just in terminal), everything works fine and the sessionFactory gets injected correctly.
When this code is on a server (Hudson), either running it through Hudson or in the console by doing ant test (test is the target to build and run the junit tests), the sessionFactory isn't injected and it looks like Autowiring isn't working. I looked at the stack trace from the nullpointerexception and noticed that it isn't using the SpringJUnit4ClassRunner even though i have it in the #RunWith annotation (on my local computer it does use it). Any ideas why? I guess it's not autowiring because it's not using SpringJUnit4ClassRunner.
Thanks