How to test Maven module project with Spring Boot - java

I have split a project, based on Spring Boot, into several Maven modules. Now only the war-project contains a starter class (having a main method, starting Spring), the other modules are of type jar.
How do I test the jar projects, if they don't include a starter?
Example JUnit test case header:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(StarterClassInDifferentProject.class)
...

I think context tests should be available per module so you can find issues with wire and configuration early on and not depend on your full application tests to find them.
I worked around this issue with a test application class in the same module.
Make sure this main class is in your test dir.
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
your context should work now.
#RunWith(SpringRunner.class)
#ActiveProfiles(profiles = {Profiles.WEB_REST})
#WebMvcTest(EntityController.class)
#DirtiesContext
public class ServicesControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private Controller controller;
#Test
public void testAll() throws Exception {
given(controller.process(null)).willReturn(null);
mvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}

I solved a similar situation.
I have a project with two modules:
a "lib" project with domain and utilities classes,
a "web" projects with a spring boot application, templates, controllers, etc...
and I wanted to test the "lib" project in a spring-boot-test fashion.
First, include the required dependencies with scope "test" in the pom.xml (in my case there is also the H2 database):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.3.3.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- add also add this here, even if in my project it is already present as a regular dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.3.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.191</version>
<scope>test</scope>
</dependency>
For testing purposes, among the test sources of the "lib" project, I have a class that acts as my test configuration
package my.pack.utils;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#TestConfiguration
#EnableJpaRepositories(basePackages = {"my.pack.engine.storage", "my.pack.storage"})
#EntityScan(basePackages = {"my.pack.storage", "my.pack.entity"})
#EnableAutoConfiguration
public class MyTestConfiguration
{
}
This sets up the H2 database in order to test the data access functionalities of the application
Finally, only in the test classes where I find it useful, I configure the execution to use the test configuration (I do not always need to do that, but sometimes it is handy):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyTestConfiguration.class)
public class TestAClassThatNeedsSpringRepositories
{
// tests...
}

The question is
How do I test the jar projects, if they don't include a starter?
I believe the right answer, is that your jar submodules should not be united tested with spring-boot context.
In fact, most if not all tests in your jar projects should not even use the RunWith(Spring...)
They should be vanilla or using a mock library such as #RunWith(MockitoJUnitRunner.class).
If you read SpringApplicationConfiguration's javadoc:
Class-level annotation that is used to determine how to load and configure an ApplicationContext for integration tests.
It is considered integration testing.
Other than that, you can also launch your tests using spring context (not spring-boot) with a 'test spring configuration' in your jar submodule. Define your beans/resources and use it in your test.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(TestConfigInJarModule.class)
For instance, I do this to test Spring data Repositories, using a test spring configuration (without dependencies on spring-boot).

Related

Building a library using Spring Boot: Unable to find #SpringBootConfiguration

I am currently working on Microservices and this project is basically a library and it does not have a main function. I am writing Unit test cases for this library, but I am getting unable to find #SpringBootConfiguration. If there is no main function, can't we execute the Unit test cases ?
The only file in the library
class OnlyFile {
void validate(){
}
}
Testing
#SpringBootTest
class MyTest {
#Test
void testIt(){
//
//
}
}
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.3.3.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
If you want to write and run unit tests and it is only a library, then I don't think it is neccesary for you to have #SpringBootTest annotation on your test class. It is used for integration testing, when you need to have a spring application context in your integration test.
When running integration tests in a class annotated with #SpringBootTest, then you need to have a class in same package or subpackage as the test class annotated with #SpringBootConfiguration, which contains the configuration to load the application context. The class could be empty if you don't need to customize the configuration. Alternatively, you can specify which components to use when loading the application context like this
#SpringBootTest(classes = ...)

#SpringBootTest for main class giving Postrges Communication Exception

I am trying to test Springboot main class for code coverage with junit5. But i am getting:
org.postgresql.util.PSQLException: Connection to 127.0.0.1:5432
refused.
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.jupiter.api.Assertions.*;
#SpringBootTest
#RunWith(SpringRunner.class)
class AlphaApplicationTest {
#Test
void main() {
assertDoesNotThrow(() -> AlphaApplication.main(new String[] {}));
}
}
Firstly, you tagged the question with junit5, so I assume you are using Junit5.
With v5, you shouldn't use the #RunWith annotation ([source])1
Secondly, you should not run your main method in the test! The SpringBootTest annotation already starts everything! Please read the documentation on testing Spring Boot Applications. When you generate a new project with start.spring.io, it will provide you with a basic unit test, which starts an application context. It should look just like this:
// Includes omitted for brevity
#SpringBootTest
class AlphaApplicationTest {
#Test
void contextLoads() {
}
}
That's all. The rest is Spring "magic".
For more, see the Spring Guides on testing, e.g., "Testing the Web Layer"
Also, for testing you usually don't want to use the "real" database. Spring Boot comes with some auto-configuration to use an H2 In-Memory-Database for testing. All you need to do is include the relevant dependencies in your POM:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
You can also use normal Spring Boot configuration for this, by using an applications.properties only for tests in test/resource/application-test.properties

Spring Integration Testing does not pick up JPA beans from other modules

I have a multi module maven project. In one module I have all my persistence stuff like entities and repositories and in another module I import that persistence module and I want to run some integration tests.
The problem is that my tests do not pick up repositories and can't create the context because of the missing beans. Can someone help me with what I am missing?
The project structure looks like this
persistence module
business logic module
web module
In my business logic module pom I import the persistence module
<dependency>
<groupId>com.acme</groupId>
<artifactId>persistence</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
My integration tests configuration
#Configuration
#ComponentScan({"com.acme.persistence", "com.acme.business"})
#EnableAutoConfiguration
public class DataTestConfig {
}
and all my tests are annotated with the following
#SpringJUnitConfig(DataTestConfig.class)
#DataJpaTest
If I move all the persistence module inside the business module then the tests work fine, but I want to keep them in separate modules.
Thanks
I found the solution.
I added #EnableJpaRepositories on my test configuration class.
This way repositories were created and now my services that used repositories can be instantiated.
This is how the final integration testing configuration looks like
#Configuration
#ComponentScan({"com.acme.persistence", "com.acme.business"})
#EntityScan("com.acme.persistence")
#EnableJpaRepositories(basePackages = "com.acme.persistence")
#EnableAutoConfiguration
public class DataTestConfig {
}

embedded mongo + spring configuration unit tests fail en masse (failed to load application context)

I'm not sure if this is a embedded mongo issue or a a spring configuration issue, maybe someone else has had the same problem.
I have a spring+maven project set up in Intellij, and have embedded mongo in test scope.
The problem: When I right click on the test java folder to run all tests, I get the dreaded application context error (see below for details). However running tests individually, or even package by package is fine. I have discovered that there is 1 test (coincidentally the first one that runs in the entire set) that is causing the break, and if I delete it I can run all tests again. Let's call this TestA. I'd like to understand why this one test causes all others to fail, and how to fix it (Since i'd really like to have that test!).
All tests extend a base class that starts with this (except for TestA):
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {Config.class, MongoConfig.class})
#WebAppConfiguration
#IntegrationTest("${port}")
#Component
TestA is slightly different because it doesn't need Mongo to run, but does need spring to autowire the services. I thought this would be ok, but it's not:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {Config.class})
#Component
MongoConfig.class is simply annotated:
#Configuration
#EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
#EnableMongoRepositories(basePackages = "<my package>")
plus the #Beans for creating the mongo process.
Config.class has these annotations and en empty body:
#Configuration
#EnableAutoConfiguration(exclude = {EmbeddedMongoAutoConfiguration.class})
#ComponentScan(value = {<list of packages>})
#EnableMongoRepositories(basePackages = {"my package"})
However if i try to run all the tests they all fail with the following error:
java.lang.IllegalStateException: Failed to load ApplicationContext
<huge stack trace eventually ending with...>
Caused by: java.nio.file.FileAlreadyExistsException: C:\Users\john\AppData\Local\Temp\extract-john-extractmongod.exe
at sun.nio.fs.WindowsFileCopy.copy(WindowsFileCopy.java:124)
at sun.nio.fs.WindowsFileSystemProvider.copy(WindowsFileSystemProvider.java:278)
at java.nio.file.Files.copy(Files.java:1274)
at de.flapdoodle.embed.process.extract.ExtractedFileSets.copy(ExtractedFileSets.java:54)
at de.flapdoodle.embed.process.store.ExtractedArtifactStore.extractFileSet(ExtractedArtifactStore.java:110)
at de.flapdoodle.embed.process.runtime.Starter.prepare(Starter.java:56)
... 192 more
My understanding is that somehow by adding TestA embedded mongo is trying to start up every time?
My temporary solution is to delete the annotations in TestA and extend the base class described above. That makes it all work fine, but isn't really correct. What I need is the right annotations on TestA for it to work in tandem with the rest of the tests.
dependencies
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.50.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
spring configuration
I don't use xml files, rather I set up class Configuration files tied up with yml files (facets added to module in Intellij).

Spring Test. Get access of test class of dependent project?

I have multi module projects; I am creating Junit Test classes for testing purpose.
But the problem is, when I run my Core Project Test using only 2 package ComponentScan for testing. It is not able to locate Util Project Test config.(Test package get excluded while building JAR)
Because of this, I am getting, No qualifying bean of type exception as Configuration class is missing from Util project.
#SpringBootApplication
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { CreateProfileTest.class })
#ComponentScan({"com.myproject.testconfig","com.myproject.module"})
#EnableAutoConfiguration
public class CreateProfileTest {
#Test
public void myMethod()
{
}
}
So, How I can gain access to Util Project Test Config in Core Project Test.
Job - > Core -> Util (Here Core is depend on Util)
Now
CoreProject/
src/main/com.myproject.config
src/main/com.myproject.module
src/test/com.myproject.testconfig
UtilPorject/
src/main/com.myproject.config
src/main/com.myproject.module
src/test/com.myproject.testconfig
Please let me know, If there is any alternative way to achieve this or if I am doing anything wrong here.
I am using Maven and Spring Boot.
It seems to me this is a matter of classpath. Have you tried adding this in the CoreProject pom.xml ?
<dependency>
<groupId>...</groupId>
<artifactId>util-project</artifactId>
<version>...</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>

Categories

Resources