Junit5 Spring Boot Autowire ComponentScan Not Working - java

My problem: if my test refers to an #Bean declaration in the class listed in #SpringBootTest, autowire works. If it refers to a class automatically #ComponentScanned by the class listed in #SpringBootTest, autowire fails. Outside of testing, my app starts without autowire or componentscan issues, and I can confirm that the service I want to load in my test runs fine from non-test. I'm frustrated as hell. Am I broken, or is Junit5 functionality on Spring Boot 2?
My Test:
#ExtendWith(SpringExtension.class)
#SpringBootTest (classes=MyConfig.class)
public class MyTest {
// fails to autowire
#Autowired
private MyService _mySvc ;
// succeeds!
#Autowired #Qualifier ( "wtf" )
private String _wtf ;
MyConfig:
#EnableWebMvc
#SpringBootApplication ( scanBasePackages = "my.packaging" )
#Configuration
public class MyConfig {
#Bean
public String wtf ( ) { return "W T F???" ; }
// No #Bean for MyService because component scan is nicer in the non-test world

I had the same issue with autowiring not working although the test was starting and the problem was that I was still using the old junit4 #Test annotation.
Make sure your test method is annotated with #Test from juni5 package org.junit.jupiter.api.Test.

I think because you have annotated as such:
#SpringBootTest (classes=MyConfig.class)
Spring will only look in MyConfig.class for the appropriate beans and is not able to find one for MyService, however, I presume that Spring will scan all packages for a bean when the application is running normally. This is why it works fine in non-test.

Like #Tudro said, org.junit.jupiter.api.Test is for Junit 5. org.junit.Test is for Junit 4.
Use this below:
#ExtendedWith(SpringExtension.class)
#SpringBootTest

Maybe you could add #ContextConfiguration(classes = ApplicationConfiguration.class)
It would look like this
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import example06.junit.group01.Piano;
#SpringBootTest
#ContextConfiguration(classes = ApplicationConfiguration.class)
public class ApplicationConfigurationTest {
#Autowired
private Piano piano;
#Test
public void shouldTestPiano() {
System.err.println("Testing JUnit5 > " + piano);
}
}
My config is a simple class
#Configuration
#ComponentScan
public class ApplicationConfiguration {
}

Related

Test annotated with #DataJpaTest not autowiring field annotated with #Autowired

I have a Spring Boot app which contains a Spring Data Jpa repository. I need to run a unit (or component?) test around this repository. I do not have a lot of experience with Spring Data Jpa.
Here's my test. It's trivially simple, and I cannot get it to pass.
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.junit.Assert.assertNotNull;
#DataJpaTest
public class FooRepositoryTest {
#Autowired
private FooRepository fooRepo;
#Test
public void notNull(){
assertNotNull(fooRepo);
}
}
Here's the other relevant source code.
import com.fedex.dockmaintenancetool.webservice.types.Foo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface FooRepository extends JpaRepository<Foo, Long> {
}
and
import javax.persistence.Entity;
#Entity
public class Foo {
}
I am just trying to get a Jpa repo autowired into a test, and I can't. Clearly I'm misunderstanding some small nuance of how Spring Boot works. But even after going through some tutorials, I cannot figure out what I'm missing. Could anyone help me with this?
When you use the annotation #DataJpaTest , it means that you are trying to test only the repository layer. The annotation is used to test JPA repositories and is used in combination with #RunWith(SpringRunner.class) to enable populating the application context. The #DataJpaTest annotation disables full auto-configuration and applies only configuration relevant to JPA tests.So as #fap siggested use it like :
#RunWith(SpringRunner.class)
#DataJpaTest
public class FooRepositoryTest {
#Autowired
private FooRepository fooRepo;
#Test
public void notNull(){
assertNotNull(fooRepo);
}
}
When you use the annotation #RunWith(SpringRunner.class) the SpringRunner provides support for loading a Spring ApplicationContext and having beans #Autowired into your test instance.
You're missing the #RunWith(SpringRunner.class) annotation that tells JUnit to actually start a Spring application for the test.
Your test class should look like
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.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertNotNull;
#RunWith(SpringRunner.class)
#DataJpaTest
public class FooRepositoryTest {
#Autowired
private FooRepository fooRepo;
#Test
public void notNull(){
assertNotNull(fooRepo);
}
}
The JUnit version used in the question is still JUnit 4.
Spring Boot 2.2.0 switches to JUnit5.
With JUnit5 you'll have to use #ExtendWith(SpringExtension.class) instead of #RunWith(SpringRunner.class). Since #JpaTest is already annotated with #ExtendsWith you don't have to actually include it though, see https://stackoverflow.com/a/65359510/4266296.

How to autowire inside a #Configuration class during JUnit tests?

I'm testing a module of my multi module spring boot project.
I'm unit testing a class annotated as #Configuration. There is a method that uses a class that is injected with #Autowired. During JUnit tests, I got a NullPointer because the Autowired is not working. How to make it work?
The application is working fine, my problem is only happening on JUnit tests.
I tried some annotations found in other answers
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {BasicConfiguration.class, SomeClass.class})
#TestPropertySource(locations= "classpath:applicationTest-configs-ok.properties")
public class BasicConfigurationTest {
#Autowired
BasicConfiguration basicConfig;
#Configuration
public class BasicConfiguration {
#Autowired
private SomeClass someClass ; <--- this is not working
#Bean
AnotherClass someMethod(){
return someClass.doSomething(); <--- nullpointer
}
#Component
SomeClass {
#Value("${test.param}")) <--- defined in applicationTest-configs-ok.properties
String myParam;
}
someClass should be injected
the example above is working, the problem was in the body where I was making some mistakes.
my bad.

#Autowired Bean is NULL in Spring Boot Unit Test

I am new to JUnit and automated testing and really want to get into automating my tests. This is a Spring Boot application. I have used Java Based Annotation style instead of XML based configuration.
I have a test class in which I'd like to test a method which retrieves a response based on a users' input.
Testing class:
#RunWith(SpringRunner.class)
#SpringBootTest
public class SampleTest(){
#Autowired
private SampleClass sampleClass;
#Test
public void testInput(){
String sampleInput = "hi";
String actualResponse = sampleClass.retrieveResponse(sampleInput);
assertEquals("You typed hi", actualResponse);
}
}
Inside my "SampleClass" I have autowired a bean like this.
#Autowired
private OtherSampleClass sampleBean;
Inside my "OtherSampleClass" I have annotated a method like so:
#Bean(name = "sampleBean")
public void someMethod(){
....
}
The issue I'm having is when I try to run the test without the #RunWith and #SpringBootTest annotations when I try to run the test my variables annotated #Autowired are null. And when I try to run the test with those annotations RunWith & SpringBootTest then I get an
IllegalStateException caused by BeanCreationException: Error creating
bean with name "sampleBean" AND failure to load application context
caused by BeanInstantiationException.
The code works 'properly' when I try to use it as a user would so I can always test this way but I think automated tests would be good for the longevity of the program.
I have used the Spring Boot Testing Docs to assist me in this.
The following config works for me.
File: build.gradle
testCompile("junit:junit:4.12")
testCompile("org.springframework.boot:spring-boot-starter-test")
File: MYServiceTest.java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
#SpringBootTest(classes = Application.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
public class MYServiceTest {
#Autowired
private MYService myService;
#Test
public void test_method() {
myService.run();
}
}
It's best to keep Spring out of your unit tests as much as possible. Instead of Autowiring your bean just create them as a regular object
OtherSampleClass otherSampleClass = mock(OtherSampleClass.class);
SampleClass sampleClass = new SampleClass(otherSampleClass);
But for this you need to use the Constructor injection instead of Field Injection which improves testability.
Replace this
#Autowired
private OtherSampleClass sampleBean;
With this
private OtherSampleClass sampleBean;
#Autowired
public SampleClass(OtherSampleClass sampleBean){
this.sampleBean = sampleBean;
}
Take a look at this answer for an other code example
No need to inject (#Autowired private SampleClass sampleClass;) your actual class which you are testing, and remove SpringBootTest annotation, SpringBootTest annotation used for integration test cases.
find the following code will help you.
#RunWith(SpringRunner.class)
public class SampleTest(){
private SampleClass sampleClass;

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.

Injecting Mockito Mock objects using Spring JavaConfig and #Autowired

I'm trying to replace an #Autowired object with a Mockito mock object. The usual way of doing this was with xml using Springockito:
<mockito:mock id="SomeMock" class="com.package.MockInterface" />
Currently I'm trying to move over to using Spring's JavaConfig to do the job. All of a sudden the Java expressions are a whole lot more verbose than xml:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class MyTestClass {
#Configuration
static class Config {
#Bean
public MockInterface somethingSpecial() {
return Mockito.mock(MockInterface.class);
}
}
#Autowired MockInterface mockObj;
// test code
}
I discovered a library called Springockito-annotations, which allows you to do the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=SpringockitoContextLoader.class)
public class MyTestClass {
#Autowired #ReplaceWithMock MockInterface mockObj;
// test code
}
Clearly, a whole lot prettier :) The only problem is that this context loader doesn't allow me to use #Configuration and JavaConfig for other beans (if I do, Spring complains that there are no candidates that match those autowired fields).
Do you guys know of a way to get Spring's JavaConfig and Springockito-annotations to play nice? Alternatively, is there another shorthand for creating mocks?
As a nice bonus, using Springockito and xml config, I was able to mock out concrete classes without providing autowiring candidates to its dependencies (if it had any). Is this not possible without xml?
Moving away from the now unmaintained (as of this writing) Spingockito-annotations and to Mockito, we have a way of doing this very simply:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#Mock MockInterface mockObj;
// test code
}
If you're using a real object, but would like to mock a dependency within it, for instance testing a service layer with DAO:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
// test code
}
Finally, this can also be applied to Spring-boot, but using annotation initialization within setUp() until multiple class runners are supported:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyMainSpringBootClass.class)
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
#Before
public final void setUp() throws Exception{
MockitoAnnotations.initMocks(this);
}
// test code
}
Outdated and deprecated!
Read about mocking and spying in Spring Boot 1.4
Please read also #ethesx answer,
Springockito is unmaintaned
Old answer
This is possible now to mock Spring application without any XML file with Springockito-annotations.. This solution works also with Spring Boot.
import static org.mockito.BDDMockito.*;
import org.kubek2k.springockito.annotations.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class,
loader = SpringockitoAnnotatedContextLoader.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MainControllerTest {
#Autowired
MainController mainController;
#Autowired
#ReplaceWithMock
FooService fooService;
#Test
public void shouldGetBar() {
//given
given(fooService.result("foo")).willReturn("bar");
//when
Bar bar build = fooService.getBar("foo");
//then
assertThat(bar).isNotNull();
}
}
Dependencies: org.kubek2k:springockito-annotations:1.0.9
It appears that SpringockitoContextLoader extends GenericXmlContextLoader which is described as:
Concrete implementation of AbstractGenericContextLoader that reads bean definitions from XML resources.
So you are limited to xml bean definitions at the moment.
You could write your own context loader, taking relevant parts from the SpringockitoContextLoader class. Take a look here to get started, perhaps you could extend AnnotationConfigContextLoader for example?

Categories