Cannot load #Configuration class in component test marked as #SpringBootTest - java

I am testing a functionality and has written a componentTest to ensure the component is working. The issue is when running the test #Configuration class is not loading for the test. Example:
TestClass:
#SpringBootTest(classes = Application.class)
public class ServiceTest{
//Do Something
}
#Configuration
public class ConfigurationClass{
#PostConstruct
public void doSomething(){
log.info("Test loading");
}
}
When running the application, I can see the logs printed on application startup. When I run the test, I don't see the logs printing from the ConfigurationClass. I tried using #ContextConfiguration(classes=ConfigurationClass.class) but no luck.
Practically , I want the configurationClass to be loaded before the tests are loaded.

When you run #SpringBootTest(classes = Application.class) with a specified class, its instructs the spring boot test engine to load only beans defined in Application.java which is supposed to (usually) be a class annotated with #Configuration annotation directly or indirectly.
If you want to just load the whole application context in the test, just use #SpringBootTest without any attributes. Now in this case it will scan packages up to one with #SpringBootConfiguration annotation (which presents on the class annotated with #SpringBootApplication and then will scan the packages down to load the configuration classes.
Of course you should make sure that the test will be able to find #SpringBootApplication class, for that you should put the test in the same package or beneath (of course the tests are in src/test/java as opposed to the src/main/java where you main class resides.

Related

How to ignore or not to load up a class that is inside ContextConfiguration?

Spring Boot related issue:
My application MyApp was based on MQ so I also wrote my tests to fit it.
Now, I have changed the application to work with Kafka instead of MQ.
My tests load up classes as part of the ApplicationContext list in the beginning of each test.
#RunWith( SpringJUnit4ClassRunner.class )
#ContextConfiguration(classes = {MyApp.class, Application.class, AppProperties.class, ...)
......
public class MyAppMainTest{
...
I have created and added few classes that related to Kafka: Producer, Properties, Client that I #Autowire inside my main class - MyApp.
#Component
public class MyApp {
#Autowired
private MyAppKafkaProducer myAppKafkaProducer;
#Autowired
private MyAppKafkaClient myAppKafkaClient;
I have added these Kafka clases to the ApplicationContext of MyAppMainTest.
#RunWith( SpringJUnit4ClassRunner.class )
#ContextConfiguration(classes = {MyAppKafkaClient.class, MyAppKafkaProducer.class, MyApp.class, ...)
....
public class MyAppMainTest {
MyAppMainTest does not test anything that related to Kafka, so basically they are useless in the test class.
The problem is that now, when the test loads up, it loads also the kafka class (because I have added them to the ApplicationContext because of the #Autowire inside the class itself), and when they loadup, they try to create a Kafka connection with localhost and then the whole test fails.
Is there any way to ignore (=not to load up) classes that exist inside the ApplicationContext list? I mean I understand that the whole point of ApplicationContext is to load up these classes and each related class, especially if its #Autowired, but in my case, they are not require inside the test class, and they cause it to fail.
So is there any way to ignore them although it's #Autowire?

Error: Unable to find #SpringBootConfiguration when doing #WebMvcTest for Spring Controller

I am testing my controller given below
#Controller
public class MasterController {
#GetMapping("/")
public String goLoginPage(){
return "index";
}
}
I am following this Spring documentation to test my controller.
Now, I want to test my controller by just instantiating the web layer and not the whole Spring context as given in the documentation. Below is my code for the same.
#RunWith(SpringRunner.class)
#WebMvcTest
public class MasterControllerTestWithWebLayer {
#Autowired
MockMvc mockMvc;
#Autowired
MasterController masterController;
#Before
public void setUp() throws Exception {
}
#After
public void tearDown() throws Exception {
}
#Test
public void testLoginHome() throws Exception{
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("index"));
}
}
When I run this test I get the error Unable to find #SpringBootConfiguration,...etc. But I am confused why it is asking for Spring configuration when we do not want it to instantiate it but want to use only the web layer. Kindly point me to the right direction what is happening here. And also how to fix this. Thanks
So here is the solution:
The documentation on detecting test configuration says:
The search algorithm works up from the package that contains the test until it finds a #SpringBootApplication or #SpringBootConfiguration annotated class. As long as you’ve structure your code in a sensible way your main configuration is usually found.
So the #SpringBootApplication class should be higher in the package hierarchy than the test class e.g if test class is in package com.zerosolutions.controller then #SpringBootApplication class should be in a package higher than com.zerosolutions.controller package i.e com.zerosolutions or com.
Problem
But in case the #SpringBootApplication class is at the same level as test class it won't be able to find it i.e com.zerosolutions.general. In this case you'll get the following error:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
Solution
If you are running an integrated test, you can explicitly mention the #SpringBootApplication class like this
#RunWith(SpringRunner.class)
#SpringBootTest(classes={SpringBootApp.class})
But if you want to do unit testing of a controller you don't need to fire up the whole Spring context. You can rather replace #SpringBootTest with #WebMvcTest(MasterController.class). This will instantiate only the web layer with MasterController and not the whole Spring context.
Problem
But the problem is you will again run into the error we faced earlier:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
And #WebMvtTest does not have a classes attribute like #SpringBootTest to explicitly mention the #SpringBootApplication class.
So there are two solutions to this.
Solution
First: Move your application class to a package higher than the test class i.e com.zerosolutions or com package.
Second: Mention your #SpringBootApplication class explicitly like below
#RunWith(SpringRunner.class)
#WebMvcTest(MasterController.class)
#ContextConfiguration(classes={SpringBootApp.class})
Hope that clears the Spring Test Configuration confusion. Thanks
If your Application.java class (in src/main/java) is located under
com.A.B
Your test class ApplicationTest.java (in src/test/java) need to be under
com.A.B or com.A.B.C or com.A.B.C.D
You will get this error if the test class is located under the following packages
com.A or com.A.C or com.A.D
In Spring boot
THE GENERAL RULE IS TEST CLASS PACKAGE NAME NEED TO START WITH THE PACKAGE NAME OF THE JAVA CLASS PACKAGE THAT IS GOING TO BE TESTED
I had the same error, and found that when I had generated the project my pom.xml showed my groupId as com.example rather than with my actual domain:
<groupId>com.example</groupId>
I corrected the pom.xml to be:
<groupId>com.mydomain</groupId>
Next, I changed the file structure from:
src/test/java/com/example to src/test/java/com/mydomain
Last, I had to update the package declaration inside my
SampleProjectApplicationTest.java
file to match the correct file structure. Once that was all in place, the tests worked fine.
I am not sure how I ended up with com.example where the rest of my project was correct, but the fix was that simple in my case.
Hopefully this helps someone.
check if src/test/java has same package name as the main class package.
src/test/java/com/example/abc is same as src/test/java/com/example/abc
Just replace the Test package name with the main Application class package name.
Example:- package com.kotlin.dealerMainApplication this is my main app package name so, I will put the same name in my Test package
This worked for me !

Disable Spring #EnableScheduling in Junit tests

I want to disable #Schedule in Spring tests but i can`t find a way to do it.
I have tried to make different config class for test environment but tasks are still fired.
This is config:
#Configuration
#EnableTransactionManagement
#EnableScheduling
#ComponentScan({"de.package"})
#PropertySource(name="application.properties", value="classpath:application.properties")
public class PersistenceJPAConfig {
...
}
This is test emvironment config.Just removed #EnableScheduling annotation
#Configuration
#EnableTransactionManagement
#ComponentScan({"de.package"})
#PropertySource(name="application.properties", value="classpath:application.properties")
public class PersistenceJPATestConfig {
...
}
In test i use:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { PersistenceJPATestConfig.class }, loader = AnnotationConfigContextLoader.class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class GetArticlesTest {
...
}
But tasks are still fired when i run the test..Is there any way to stop executing tasks while running tests ?
As you're using #ComponentScan on the same package both time, it seems spring is loading the other configuration too.
You could use some profile to filter that, like adding this on your PersistenceJPATestConfig
#Profile("test")
add this annotation on your JUnit class so it will be executed with the "test" profile
#ActiveProfiles("test")
Edit :
Your main config should also be profiled so it is ignored when its profile is not active, so you should add another #Profile on the main config class with a different profile than "test"
Quick solution based on other answers(for spring boot users), just add below code to main configuration so that it wont run on test profiles and test cases! No other changes!
#Profile(!test)
public class Config{
......
}

Unable to find a #SpringBootConfiguration when doing a JpaTest

I'm trying to run a simple Junit test to see if my CrudRepositories are indeed working.
The error I keep getting is:
Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
java.lang.IllegalStateException
Doesn't Spring Boot configure itself?
My Test Class:
#RunWith(SpringRunner.class)
#DataJpaTest
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class JpaTest {
#Autowired
private AccountRepository repository;
#After
public void clearDb(){
repository.deleteAll();
}
#Test
public void createAccount(){
long id = 12;
Account u = new Account(id,"Tim Viz");
repository.save(u);
assertEquals(repository.findOne(id),u);
}
#Test
public void findAccountByUsername(){
long id = 12;
String username = "Tim Viz";
Account u = new Account(id,username);
repository.save(u);
assertEquals(repository.findByUsername(username),u);
}
My Spring Boot application starter:
#SpringBootApplication
#EnableJpaRepositories(basePackages = {"domain.repositories"})
#ComponentScan(basePackages = {"controllers","domain"})
#EnableWebMvc
#PropertySources(value {#PropertySource("classpath:application.properties")})
#EntityScan(basePackages={"domain"})
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
My Repository:
public interface AccountRepository extends CrudRepository<Account,Long> {
public Account findByUsername(String username);
}
}
Indeed, Spring Boot does set itself up for the most part. You can probably already get rid of a lot of the code you posted, especially in Application.
I wish you had included the package names of all your classes, or at least the ones for Application and JpaTest. The thing about #DataJpaTest and a few other annotations is that they look for a #SpringBootConfiguration annotation in the current package, and if they cannot find it there, they traverse the package hierarchy until they find it.
For example, if the fully qualified name for your test class was com.example.test.JpaTest and the one for your application was com.example.Application, then your test class would be able to find the #SpringBootApplication (and therein, the #SpringBootConfiguration).
If the application resided in a different branch of the package hierarchy, however, like com.example.application.Application, it would not find it.
Example
Consider the following Maven project:
my-test-project
+--pom.xml
+--src
+--main
+--com
+--example
+--Application.java
+--test
+--com
+--example
+--test
+--JpaTest.java
And then the following content in Application.java:
package com.example;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Followed by the contents of JpaTest.java:
package com.example.test;
#RunWith(SpringRunner.class)
#DataJpaTest
public class JpaTest {
#Test
public void testDummy() {
}
}
Everything should be working. If you create a new folder inside src/main/com/example called app, and then put your Application.java inside it (and update the package declaration inside the file), running the test will give you the following error:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
Configuration is attached to the application class, so the following will set up everything correctly:
#SpringBootTest(classes = Application.class)
Example from the JHipster project here.
It is worth to check if you have refactored package name of your main class annotated with #SpringBootApplication. In that case the testcase should be in an appropriate package otherwise it will be looking for it in the older package . this was the case for me.
It work fo me
the package name of the above test class is changed to the same as the package name of the normal class.
change to this
In addition to what Thomas Kåsene said, you can also add
#SpringBootTest(classes=com.package.path.class)
to the test annotation to specify where it should look for the other class if you didn't want to refactor your file hierarchy. This is what the error message hints at by saying:
Unable to find a #SpringBootConfiguration, you need to use
#ContextConfiguration or #SpringBootTest(classes=...) ...
In my case the packages were different between the Application and Test classes
package com.example.abc;
...
#SpringBootApplication
public class ProducerApplication {
and
package com.example.abc_etc;
...
#RunWith(SpringRunner.class)
#SpringBootTest
public class ProducerApplicationTest {
After making them agree the tests ran correctly.
I had the same issue and I solved by adding an empty class annotated with SpringBootApplication in the root package of the folder src/test/java
package org.enricogiurin.core;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class CoreTestConfiguration {}
The test slice provided in Spring Boot 1.4 brought feature oriented test capabilities.
For example,
#JsonTest provides a simple Jackson environment to test the json serialization and deserialization.
#WebMvcTest provides a mock web environment, it can specify the controller class for test and inject the MockMvc in the test.
#WebMvcTest(PostController.class)
public class PostControllerMvcTest{
#Inject MockMvc mockMvc;
}
#DataJpaTest will prepare an embedded database and provides basic JPA environment for the test.
#RestClientTest provides REST client environment for the test, esp the RestTemplateBuilder etc.
These annotations are not composed with SpringBootTest, they are combined with a series of AutoconfigureXXX and a #TypeExcludesFilter annotations.
Have a look at #DataJpaTest.
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#BootstrapWith(SpringBootTestContextBootstrapper.class)
#OverrideAutoConfiguration(enabled = false)
#TypeExcludeFilters(DataJpaTypeExcludeFilter.class)
#Transactional
#AutoConfigureCache
#AutoConfigureDataJpa
#AutoConfigureTestDatabase
#AutoConfigureTestEntityManager
#ImportAutoConfiguration
public #interface DataJpaTest {}
You can add your #AutoconfigureXXX annotation to override the default config.
#AutoConfigureTestDatabase(replace=NONE)
#DataJpaTest
public class TestClass{
}
Let's have a look at your problem,
Do not mix #DataJpaTest and #SpringBootTest, as said above #DataJpaTest will build the configuration in its own way(eg. by default, it will try to prepare an embedded H2 instead) from the application configuration inheritance. #DataJpaTest is designated for test slice.
If you want to customize the configuration of #DataJpaTest, please read this official blog entry from Spring.io for this topic,(a little tedious).
Split the configurations in Application into smaller configurations by features, such as WebConfig, DataJpaConfig etc. A full featured configuration(mixed web, data, security etc) also caused your test slice based tests to be failed. Check the test samples in my sample.
In my case
Make sure your (test package name) of YourApplicationTests is equivalent to the (main package name).
When all the classes were in same package, test classes were working. As soon as I moved all the java classes to different package to maintain proper project structure I was getting the same error.
I solved it by providing my main class name in the test class like below.
#SpringBootTest(classes=JunitBasicsApplication.class)
I think that the best solution for this issue is to align your tests folders structure with the application folder structure.
I had the same issue which was caused by duplicating my project from a different folder structure project.
if your test project and your application project will have the same structure you will not be required to add any special annotations to your tests classes and everything will work as is.
Make sure the test class is in a sub-package of your main spring boot class
This is more the the error itself, not answering the original question:
We were migrating from java 8 to java 11. Application compiled successfully, but the errors Unable to find a #SpringBootConfiguration started to appear in the integration tests when ran from command line using maven (from IntelliJ it worked).
It appeared that maven-failsafe-plugin stopped seeing the classes on classpath, we fixed that by telling failsafe plugin to include the classes manually:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${basedir}/target/classes</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
...
</plugin>
In my case I was using the Test class from wrong package.
when I replaced import org.junit.Test; with import org.junit.jupiter.api.Test; it worked.
None of the suggested solutions worked because I don't have #SpringBootApplication in the system. But I figured it out - this is how I fixed:
// AppConfig.java
#Configuration
#ComponentScan("foo")
#ConfigurationPropertiesScan
public class AppConfig {
// ...
}
// HelloWorld.java
#Service
public class HelloWorld {
public void foo() {
// ...
}
}
// HelloWorldTest.java
#SpringBootTest(classes = { AppConfig.class, HelloWorld.class })
public class HelloWorldTest {
#Autowired private HelloWorld helloWorld;
#Test
public testFoo() {
this.helloWorld.foo(); // Testing logic
}
}
The key is including both AppConfig.class and HelloWorld.class in #SpringBootTest.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#DataJpaTest
#SpringBootTest
#AutoConfigureWebMvc
public class RepoTest {
#Autowired
private ThingShiftDetailsRepository thingShiftDetailsRepo;
#Test
public void findThingShiftDetails() {
ShiftDetails details = new ShiftDetails();
details.setThingId(1);
thingShiftDetailsRepo.save(details);
ShiftDetails dbDetails = thingShiftDetailsRepo.findByThingId(1);
System.out.println(dbDetails);
}
}
Above annotations worked well for me. I am using spring boot with JPA.

Spring ContextConfiguration Initializers not run under maven test

I have a Junit test running flawlessely within a Spring application context. The application context includes two xml files as well as an initializer. The JUnit base class is as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration( locations = { "classpath:/applicationContext.xml",
"classpath:/applicationcontext-security.xml"},
initializers = com.abc.ConfigurationInitializer.class )
#TransactionConfiguration
#Transactional
public abstract class BaseTest extends AbstractTransactionalJUnit4SpringContextTests
{......
However when running the test under maven the application context fails to load and this is because the initializer class is not run.
Why is the Initializer(s) class not being run?
My bad - The initializer ran it simply crashed very soon thereafter

Categories