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.
Related
I'm working on a legacy project that have no tests. We want to add integration tests for our services.
The services communicates with other services and with the database as well.
I could handle to run junit tests so I can mock service calls and database calls to return whatever I want but I'm wondering if it's possible to run an actual integration test where it communicate with the actual dev database and with the other services in the project as well.
The beans are defined in xml files with some placeholders on it.
I'm looking for a direction on what to look for and if it is possible at all on this spring version.
Thanks!
To configure your test can you make first class SwitchCase and extend this in test class.
import java.util.Random;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
public class SwitchCase{
*** here you can have tour variables and do #Autowired in yours service
}
public class YoursTestClass extends SwitchCase{
#Test
public void save() throws Exception {
}
}
To do mocks you can use Mockito.
#WebMvcTest
#AutoConfigureMockMvc
public class YouClass {
#Autowired
MockMvc mock
MockHttpServelet request = MockMvcRequestBuilders
.post("http/....")
.contentTupe(**pass yout content type **);
mock.perform(request)
.andExpect(MockMvcResultMatchers.status().isCreated())
.andExpect(MockMvcResultMatchers.jsonPath("id").value(7)
}
It is just an example to guide, but it would be good to take a look at the mockito documentation
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 {
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:spring/integration-test.xml"
})
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class TestClass {
#Autowired
private Dao dao;
#Test
#Transactional
public void test1() {
}
}
Hello!
I'm trying to start single test method from Spring Tool Suite Debug configurations and I have an exception:
java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=test1], {ExactMatcher:fDisplayName=test1(com.org.TestClass )], {LeadingIdentifierMatcher:fClassName=com.org.TestClass ,fLeadingIdentifier=test1]] from org.junit.internal.requests.ClassRequest#302e67
at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:40)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createFilteredTest(JUnit4TestLoader.java:77)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:68)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:43)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:444)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
But since I remove #RunWith(SpringJUnit4ClassRunner.class) at this example the test method launched successfully.
I would appreciate any useful advice to eliminate this trouble.
Thank you.
I have spring version 3.1.1
Here are imports:
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.testng.annotations.Test;
It is cause spring configuration specific works.
Spring created proxy instance for your class because of #Transactional annotation under your test method, and after that method doesn't contain #Test annotation.
Please see information about spring proxy there: https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring
Do you really need #Transactional annotation under your test method?
You can create class-helper only for testing, write #Transactional annotated methods there, inject it (annotation #Autowired) and use in your test class
Hey i have started learing spring-boot junit testing using spring boot Test framework at the time of creating the test case i am facing issues below .
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ApplicationTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")));
}
}
In Above Code i am getting Error of
Caused by: **org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.springframework.test.web.servlet.MockMvc' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}**
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1486) ~[spring-beans-4.3.8.RELEASE.jar:4.3.8.RELEASE]
I am aware of MockMvc name bean is not found by spring-boot but why its not able to find it and how i can do so that application will work fine.
Hope you have spring-boot-starter-web dependency. Not sure which version of Spring boot you use, but build mockMvc this way instead?
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
Try adding the following annotations to the class.
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
I had the same issue, because I'm following a tutorial using WebFlux (reactive Web) instead of synchronous Web. Assuming that unit tests can be synchronous, finally this worked for me (in Gradle)
implementation 'org.springframework.boot:spring-boot-starter-webflux'
// Required for MockMvc autoconfigure
testImplementation 'org.springframework.boot:spring-boot-starter-web'
I also had the same issue because I forgot to remove the following line from the application.properties file in the test folder:
spring.main.web-application-type=none
Simply removing it solved the problem.
I believe that the answer of #karthik-r is the one!
I also had the same problem, so one thing that helps to "debug" what was beeing injected by Spring was that.
#BeforeEach
void printApplicationContext() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
//This
Arrays.stream(webApplicationContext.getBeanDefinitionNames())
.map(name -> webApplicationContext.getBean(name).getClass().getName())
.sorted()
.forEach(System.out::println);
}
Of course if you have
#Autowired
private WebApplicationContext webApplicationContext;
This will print all spring injected beans.
And finaly one more thing that i had to modify and if you are using JUnit 5.
Change the #RunWith to
#ExtendWith(SpringExtension.class)
That is it...
=)
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;