234

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);

    }
}
starball
  • 20,030
  • 7
  • 43
  • 238
Thomas Billet
  • 2,381
  • 2
  • 13
  • 13

16 Answers16

347

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

Thomas Kåsene
  • 5,301
  • 3
  • 18
  • 30
  • 1
    Greetings, thank you for offering up a solution Am using the Maven packet setup , with a different package for tests and the application code. If i interpret right what u are saying is , That I have to direct my Test Package to the Application class? and then it will find the SpringConfiguration? – Thomas Billet Aug 22 '16 at 17:28
  • If by "maven packet" you mean "module", then yes, the module in which your test class is has to depend on the module that `Application` is in. If, however, you mean `src/main` and `src/test`, then those folders are not part of the package hierarchy. Perhaps you are better off just updating your question with a screenshot or an explanation of what your project structure is like. – Thomas Kåsene Aug 22 '16 at 17:34
  • 3
    +--test +--com +--example +--JpaTest.java also work – user674158 Mar 09 '17 at 08:04
  • @ThomasBillet What project structure did you end up using? From what I found is that the default structure is `src/main/java` and `src/test/java` – Bernie Lenz Aug 28 '17 at 20:00
  • @ThomasKåsene in my case same error occurs as this question when I try to write tests for service layer, and I have structure like https://softwareengineering.stackexchange.com/a/323746/214216 Can you please suggest the way to write unit tests for this structure? Thanks much. – Foolish Sep 16 '17 at 18:31
  • I'm afraid that's too little to go on, and I probably wouldn't have been able to give a sufficiently good answer here in the comment field anyway. I suggest posting a new question where you show us your project's structure, what you're trying to accomplish, and the exact error message you're getting. Thanks! – Thomas Kåsene Sep 17 '17 at 18:27
150

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.

mrts
  • 16,697
  • 8
  • 89
  • 72
  • 1
    This seems to be the perfect solution. I don't need to move any class or folder. – Abhishek Aggarwal Feb 02 '20 at 03:37
  • I am still getting ClassNotFound exception even when the Application class does exist. Also your link is not valid anymore. – Praytic Oct 15 '20 at 10:31
  • @Praytic, thanks for the heads-up regarding the invalid link! I updated it just now. As for the ClassNotFound exception, check out the example at the link. – mrts Oct 15 '20 at 13:18
  • 2
    This is just perfect! To be more precise, this solves the question that Thomas raised on his post _If the application resided in a different branch of the package hierarchy, however, like com.example.application.Application, it would not find it._. – Shawn. L Jul 08 '21 at 15:27
  • 1
    Just for making sure, here `Application.class` is the main class, right? – Sanal S Aug 26 '22 at 11:20
26

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.

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Mohammed Rafeeq
  • 2,586
  • 25
  • 26
19

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

hang gao
  • 421
  • 5
  • 6
14

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=...) ...
Cameron Gagnon
  • 1,532
  • 17
  • 18
10

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.

Nick
  • 328
  • 2
  • 10
8

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 {}
Enrico Giurin
  • 2,183
  • 32
  • 30
  • 1
    That is correct, it is just what I was looking for, I have a multimodular spring project where I moved the tests to another module, but just this class did the correct execution of the tests in that module. – 0x52 Apr 16 '21 at 18:43
  • Ditto - this is the only solution I've seen work for a multi-module gradle project. – Dan Tanner Aug 14 '22 at 03:51
7

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,

  1. 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.
  2. If you want to customize the configuration of @DataJpaTest, please read this official blog entry from Spring.io for this topic,(a little tedious).
  3. 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.
Hantsy
  • 8,006
  • 7
  • 64
  • 109
4

In my case
Make sure your (test package name) of YourApplicationTests is equivalent to the (main package name).

Ahmed Nabil
  • 17,392
  • 11
  • 61
  • 88
2

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)
kaybee99
  • 4,566
  • 2
  • 32
  • 42
2

Make sure the test class is in a sub-package of your main spring boot class

Edor Linus
  • 826
  • 8
  • 9
2

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>
Michal Lonski
  • 877
  • 1
  • 11
  • 20
1

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.

silver
  • 1,633
  • 1
  • 20
  • 32
  • I had the same error and I finally figured out that the package name for the test class had a typo "rule" in place of "rules". After fixing the package name, error went away. – Gopal Bairwa Oct 29 '19 at 15:48
0

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.

happy
  • 2,550
  • 17
  • 64
  • 109
0

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.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
KimchiMan
  • 4,836
  • 6
  • 35
  • 41
-4
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.

Umesh G
  • 3
  • 1
  • 1