I have a spring boot application with one controller class, one Service and one repository working perfectly fine. I have added Junit test cases for the same and that is also working perfectly fine.
#RestController
public class EmployeeController{
#Autowired
EmployeeService service;
#GetMapping("/list")
public List<Employee> getEmployee(){
return service.findAll();
}
}
#Service
public class EmployeeService{
#Autowired
EmployeeRepository repository;
public List<Employee> findAll(){
return repository.findAll();
}
}
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, String>{}
The test class is below.
#RunWith(SpringRunner.class)
#WebMvcTest(value = EmployeeController.class)
class EmployeeControllerIntegrationTest {
#Autowired
private MockMvc mvc;
#MockBean
private EmployeeService service;
#Test
public void findAllEmployeeTest(){
}
}
The test case is passing until here, but at the moment I am adding another API as below all tests are failing.
#RestController
public class DepartmentController{
#Autowired
DepartmentService service;
#GetMapping("/list")
public List<Department> getDepartment(){
return service.findAll();
}
}
#Service
public class DepartmentService{
#Autowired
DepartmentRepository repository;
public List<Department> findAll(){
return repository.findAll();
}
}
#Repository
public interface DepartmentRepository extends JpaRepository<Department, String>{}
The test class is below.
#RunWith(SpringRunner.class)
#WebMvcTest(value = DepartmentController.class)
class DepartmentControllerIntegrationTest {
#Autowired
private MockMvc mvc;
#MockBean
private DepartmentService service;
#Test
public void findAllDepartmentTest(){
}
}
After adding the Department services test cases are failing with below error:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'departmentController': Unsatisfied dependency expressed through field 'departmentService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'departmentService': Unsatisfied dependency expressed through field 'repository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.employeeapp.data.repository.DepartmentRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Cheers!
For what i see, you try to do an IT test by mocking your service. In IT test you don't mock your service. In Integration Test you test that all work together. If you don't mock the repository it's more an E2E Test (end to end you test all the interaction path).
You can mock the repository if there is no logic to test, and/or don't want to write in your database.
So if it's unit test you can try that (it work on my projects) and i use Junit 5 (org.junit.jupiter)
#WebMvcTest(DepartementController.class)
public class DepartementControllerTest {
#MockBean
private DepartmentService departmentService;
#Autowired
MockMvc mockMvc;
#Test
public void findAllTest(){
// You can create a list for the mock to return
when(departmentService.findAll()).thenReturn(anyList<>);
//check if the controller return something
assertNotNull(departmentController.findAll());
//you check if the controller call the service
Mockito.verify(departmentService, Mockito.times(1)).findAll(anyList());
}
That's a unit test.
An It test will be more like
#ExtendWith(SpringExtension.class)
#SpringBootTest
public class DepartmentIT {
#Autowired
DepartmentService departmentService;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#BeforeEach
void setUp() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.wac)
.build();
}
#Test
void departmentFindAll() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/YOUR_CONTROLLER_URL")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is(200))
.andReturn();
assertTrue(mvcResult.getResponse().getContentAsString().contains("SOMETHING_EXPECTED"));
assertTrue(mvcResult.getResponse().getContentAsString().contains("SOMETHING_EXPECTED"));
assertTrue(mvcResult.getResponse().getContentAsString().contains("SOMETHING_EXPECTED"));
//You also can check its not an empty list, no errors are thrown etc...
}
the imports use so you dont search too much are
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
No qualifying bean of type 'com.employeeapp.data.repository.DepartmentRepository' available: expected at least 1 bean which qualifies as autowire candidate.
Spring looks for the bean of TYPE: DepartmentRepository.
Define the Bean
#Repository
public interface DepartmentRepository extends JpaRepository<Department, String>{}
Add the annotation here #Repository so Spring can turn DepartmentRepository into a Bean.
Autowire the Correct Type and Name
#Service
public class DepartmentService{
#Autowired
private DepartmentRepository repository; // The best practice is to write the full bean name here departmentRepository.
public List<Department> findAll(){
return repository.findAll();
}
}
Related
In my Java application I am trying to test the following service method with a Unit Test:
#Service
#EnableCaching
#RequiredArgsConstructor
public class LabelServiceImpl implements LabelService {
private static final String CACHE_NAME = "demoCache";
private final LabelRepository labelRepository;
#Override
public List<LabelDTO> findByUuid(UUID uuid) {
final Label label = labelRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException("Not found."));
final List<LabelDTO> labelList = labelRepository.findByUuid(uuid);
return labelList;
}
}
Here is my Unit Test:
#Import({CacheConfig.class, LabelServiceImpl.class})
#ExtendWith(SpringExtension.class)
#EnableCaching
#ImportAutoConfiguration(classes = {
CacheAutoConfiguration.class,
RedisAutoConfiguration.class
})
#RunWith(MockitoJUnitRunner.class)
public class CachingTest {
#InjectMocks
private LabelServiceImpl labelService;
#Autowired
private CacheManager cacheManager;
#Mock
private LabelRepository labelRepository;
#Test
void givenRedisCaching_whenFindUuid_thenReturnFromCache() {
//code omitted
LabelDTO labelFromDb = labelService.findByUuid(uuid);
LabelDTO labelFromCache = labelService.findByUuid(uuid);
verify(labelRepository, times(1)).findByUuid(uuid);
}
}
When I use #Autowired for LabelServiceImpl in the Unit Test, I get "Nullpointer exception" error. If I use #InjectMocks annotation for that instance, then there is no error, but no caching is done (it calls labelService.findByUuid 2 times instead of 1).
I think I made a mistake related to the annotations in Unit Test class, but I tried many different combinations and cannot solve the problem.
So, how can I fix the problem?
Update: Finally I fixed the problem by using the following approach. However, I also had to update the mockito-core version from 3.3.3 to 3.7.0 as shown below. Otherwise I was getting "java.lang.NoSuchMethodError: org.mockito.MockitoAnnotations.openMocks(Ljava/lang/Object;)Ljava/lang/AutoCloseable;" error.
pom.xml:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
CachingTest:
#ExtendWith(MockitoExtension.class)
#SpringBootTest
public class CachingTest {
#Autowired
#InjectMocks
private LabelServiceImpl labelService;
#MockBean
private LabelRepository labelRepository;
#Test
public void givenRedisCaching_whenFindUuid_thenReturnFromCache() {
//code omitted
LabelDTO labelFromDb = labelService.findByUuid(uuid);
LabelDTO labelFromCache = labelService.findByUuid(uuid);
verify(labelRepository, times(1)).findByUuid(uuid);
assertEquals(labelFromDb, labelFromCache);
}
}
Mockito does not simulate your springt boot application. It just mocks the annotated objects and injects your mock objects into your labelService.
On the other hand to test a spring boot application #Autowired is not enough.
You have to build a Spring Boot Integration Test.
To do this annotate your Test with #SpringBootTest.
This initializes a real Application Context.
try to autowire, LabelService
package com.demo;
import org.springframework.stereotype.Service;
#Service
public class LabelService
{
public String getLabel()
{
return "Test Label " + System.currentTimeMillis();
}
}
package com.demo;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
public class CachingTest
{
#Autowired
private LabelService labelService;
#Test
public void testLabelService()
{
assertNotNull(labelService);
}
}
this works
I am unable to get a Spring Rest Docs test with JUnit 5 and Webflux working.
I have a working integration test with #WebFluxTest like this:
#WebFluxTest(SomeController.class)
class SomeControllerTest {
#Autowired
private WebTestClient testClient;
#MockBean
private SomeService service;
#Test
void testGetAllEndpoint() {
when(service.getAll())
.thenReturn(List.of(new Machine(1,"Machine 1", "192.168.1.5", 9060)));
testClient.get().uri("/api/machines")
.exchange()
.expectStatus().isOk()
.expectBodyList(Machine.class)
.hasSize(1);
}
}
I now want to write a documentation test. According to the docs, something like this should work:
#SpringBootTest
#ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
class SomeControllerDocumentation {
private WebTestClient testClient;
#MockBean
private SomeService service;
#BeforeEach
public void setUp(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.testClient = WebTestClient.bindToApplicationContext(webApplicationContext)
.configureClient()
.filter(documentationConfiguration(restDocumentation))
.build();
}
#Test
void testGetAllEndpoint() {
when(service.getMachines())
.thenReturn(List.of(new Machine(1, "Machine 1", "192.168.1.5", 9060)));
testClient.get().uri("/api/machines")
.accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk()
.expectBody().consumeWith(document("machines-list"));
}
}
I however get:
org.junit.jupiter.api.extension.ParameterResolutionException:
Failed to resolve parameter [org.springframework.web.context.WebApplicationContext webApplicationContext]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.springframework.web.context.WebApplicationContext' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I am also wondering if #SpringBootTest is needed as annotation or if #WebFluxTest(SomeController.class) is also supposed to work.
I am using Spring Boot 2.1.3 with spring-restdocs-webtestclient as dependency.
Instead of injecting WebApplicationContext use this:
#Autowired
private ApplicationContext context;
#BeforeEach
void setUp(RestDocumentationContextProvider restDocumentation) {
client = WebTestClient.bindToApplicationContext(context)
.configureClient()
.filter(documentationConfiguration(restDocumentation))
.build();
}
As an alternative to the answer of Gilad Peleg, you can just change the type in the method argument from WebApplicationContext to ApplicationContext.
Note also that instead of #SpringBootTest, you can use #WebFluxTest(SomeController.class)
According to docs:
#AutoConfigureRestDocs can also be used with WebTestClient. You can
inject it by using #Autowired and use it in your tests as you normally
would when using #WebFluxTest and Spring REST Docs, as shown in the
following example:
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
#RunWith(SpringRunner.class)
#WebFluxTest
#AutoConfigureRestDocs
public class UsersDocumentationTests {
#Autowired
private WebTestClient webTestClient;
}
Running a test class throws the following exception:
BeanNotOfRequiredTypeException: Bean named 'myServiceImpl' is expected to be of type 'MyServiceImpl' but was actually of type 'com.sun.proxy.$Proxy139'
This error gets thrown only with unit tests, the program itself works.
My Interface
public interface MyService {
public String testMethod();
}
My Implementation
#Service
public class MyServiceImpl implements MyService{
#Autowired
private TransactionRepository transactionRepo;
#Autowired
private AccountRepository accountRepository;
#Autowired
private BankAccountStatementFactory baStatementFactory;
public String myMethod() {
return "Run My Method";
}
}
My Unit Test
#RunWith(SpringRunner.class)
#SpringBootTest
public class DeleteMeTest{
#Mock
private TransactionRepository transactionRepo;
#Mock
private AccountRepository accountRepository;
#Mock
private BankAccountStatementFactory baStatementFactory;
#InjectMocks
#Resource
MyServiceImpl myService;
#org.junit.Before
public void setUp() throws Exception {
// Initialize mocks created above
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
myService.myMethod();
System.out.println("My Unit Test");
}
}
Running this test class throws the following exception:
BeanNotOfRequiredTypeException: Bean named 'myServiceImpl' is expected to be of type 'MyServiceImpl' but was actually of type 'com.sun.proxy.$Proxy139'
A solution here is to inject the Interface, not the implementation into the unit test but this will not allow me to inject mocks.
Thats because an implementation is required with the #InjectMocks annotation. When I try to inject mocks into the interface I get the following exception:
Cannot instantiate #InjectMocks field named 'myService'! Cause: the type 'MyService' is an interface.
Just to be clear, all this worked in the beginning and went bad once I repackaged my classes. That could be the cause but not 100% sure.
Any hints on what might cause this BeanNotOfRequiredTypeException?
Thanks!
Since it's a unit test, you don't need Spring IMHO.
Simply initialize the tested class this way:
#InjectMocks
MyServiceImpl myService = new MyServiceImpl();
You can also remove these annotations:
#RunWith(SpringRunner.class)
#SpringBootTest
If you really need to use Spring (the reason for using Spring in the unit test is not clear from your post), you can try to unproxy the bean:
Have a separate declaration for the proxy and the bean:
#Resource
MyServiceImpl proxy;
#InjectMocks
MyServiceImpl myService;
Then initialize them in setUp():
#org.junit.Before
public void setUp() throws Exception {
// Initialize mocks created above
myService = (MyServiceImpl)((TargetSource) proxy).getTarget();
MockitoAnnotations.initMocks(this);
}
I'm trying to write an integration test but I'm having trouble with autowiring the repository in the test.
I receive this exception:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.observer.media.repository.ArticleRepository]: Specified class is an interface.
Edit:
I added PersistenceConfig.class with #EnableJpaRepositories, code below. This results in Exception org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
I also tried to add Application.class in #SpringBootTest(classes = {} in an catch all attempt but that throws Error creating bean with name 'articleRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class org.observer.media.model.Article
ScraperRunnerIntegrationTest (the config classes only contain beans of domain classes):
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {
ApplicationConfig.class,
PersistenceConfig.class,
DeMorgenTestConfig.class,
Article.class,
ScraperRunner.class,
DeMorgenWebsiteScraper.class,
ArticleService.class,
DeMorgenPageScraper.class,
JsoupParser.class,
DeMorgenArticleScraper.class,
GenericArticleScraper.class,
ImageMetaScraper.class,
ArticlePrinter.class,
ArticleRepository.class
})
public class ScraperRunnerIntegrationTest {
private final static Article EXPECTED_ARTICLE_1 = anArticle().withHeadline("headline1").build();
private final static Article EXPECTED_ARTICLE_2 = anArticle().withHeadline("headline2").build();
#Autowired
private ScraperRunner scraperRunner;
#Autowired
private DeMorgenWebsiteScraper deMorgenWebsiteScraper;
#Autowired
private ArticleRepository articleRepository;
#Test
public void run() {
scraperRunner.run(deMorgenWebsiteScraper);
assertThat(articleRepository.findAll()).containsOnly(EXPECTED_ARTICLE_1, EXPECTED_ARTICLE_2);
}
Repository:
import org.observer.media.model.Article;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface ArticleRepository extends JpaRepository<Article, Long> {
List<Article> findAll();
Article findByHash(String hash);
Article findByHeadline(String headline);
List<Article> findArticleByHeadlineAndCompanyName(String headline, String companyName);
#Query("SELECT CASE WHEN COUNT(a) > 0 THEN true ELSE false END FROM Article a WHERE a.hash = :hash")
boolean existsByHash(#Param("hash") String hash);
}
PersistenceConfig.class:
package org.observer.media.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#Configuration
#EnableJpaRepositories("org.observer.media.repository")
public class PersistenceConfig {
}
You need to provide only the classes, annotated as #Configuration to #SpringBootTest.
I have modified the original example from here to use the #SpringBootTest annotation.
So the following configuration works:
#Configuration
#ComponentScan("hello")
public class AppConfig {
}
Note the #ComponentScan annotation.
And then in my test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes={AppConfig.class})
public class CustomerRepositoryTests {
#Autowired
private CustomerRepository customerRepository;
}
And it did a trick. You can try to do the same in your example.
I had the same exception org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Specified class is an interface on my integration test.
I had resolved without using a PersistenceConfig.class but changed only the annotaion used in my integration class that was wrong. I have used the following annotation:
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
because I didn't want use a in-memory database.
Your code could be the following:
#ComponentScan(basePackages = {"org.observer"})
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class ScraperRunnerIntegrationTest {
private final static Article EXPECTED_ARTICLE_1 = anArticle().withHeadline("headline1").build();
private final static Article EXPECTED_ARTICLE_2 = anArticle().withHeadline("headline2").build();
#Autowired
private ScraperRunner scraperRunner;
#Autowired
private DeMorgenWebsiteScraper deMorgenWebsiteScraper;
#Autowired
private ArticleRepository articleRepository;
#Test
public void run() {
scraperRunner.run(deMorgenWebsiteScraper);
assertThat(articleRepository.findAll()).containsOnly(EXPECTED_ARTICLE_1, EXPECTED_ARTICLE_2);
}
Maybe you can try #AutoConfigureDataJpa or #AutoConfigureXxx annotations.
I know I should not be testing void methods like this, but I am just testing Mockito.doNothing() as of now with a simple example.
My Service class:
#Service
public class Service{
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscribtions(){
kafkaConsumer.unsubscribe();
}
}
My Test class:
#MockBean
private Consumer<String, String> kafkaConsumer;
#Test
public void testClearSubscriptions() {
Service service = new Service();
Mockito.doNothing().when(kafkaConsumer).unsubscribe();
service.clearSubscriptions();
}
The test keeps failing with a null pointer exception. When I debugged it, it goes into the clearSubscription method of the service class, and there on the line of kafkaConsumer.unsubscribe(), kafkaConsumer is null. But I mocked the consumer, why is it throwing null pointer exception and I should be skipping over that method, right?
Edit:
All the declarations of the class:
#Autowired
private Consumer<String, String> kafkaConsumer;
#Autowired
private Service2 service2;
private final Object lock = new Object();
private static Logger logger = LoggerFactory.getLogger(Service.class);
private HashMap<String, String> subscribedTopics = new HashMap<>();
Figured out what was wrong, I needed to auto wire the service
You are instantiating a new service Service service = new Service(); but from what I can see you are never injecting the mock bean into the new service.
Here is a sample of what I think you could do if you are using mockito only and dont need to instantiate a spring container (used a single class for ease of example dont do this in actual code):
package com.sbp;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#RunWith(MockitoJUnitRunner.class) // run with mockitos runner so annotations are processed
public class MyServiceTest {
public interface Consumer<T, R> {
public void unsubscribe();
}
#Service
public class KafkaConsumer implements Consumer<String, String> {
#Override
public void unsubscribe() {
}
}
#Service
public class MyService {
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscriptions() {
kafkaConsumer.unsubscribe();
}
}
#Mock // tell mockito that this is a mock class - it will instantiate for you
private Consumer<String, String> kafkaConsumer;
#InjectMocks // tell mockito to inject the above mock into the class under test
private MyService service = new MyService();
#Test
public void testClearSubscriptions() {
service.clearSubscriptions();
Mockito.verify(kafkaConsumer, Mockito.times(1)).unsubscribe();
}
}
If you need an example via Spring using MockBean or without and dependencies, let me know and I can post.
UPDATED: adding sample using spring junit runner and using spring boot's mockbean annotation
package com.sbp;
import com.sbp.MyServiceTest.TestContext.MyService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class) // run with spring
#SpringBootTest(classes = MyServiceTest.TestContext.class) // make it a spring boot test so #MockBean annotation is processed, provide a dummy test context class
public class MyServiceTest {
public interface Consumer<T, R> {
public void unsubscribe();
}
#Configuration
public static class TestContext {
#Service
public class KafkaConsumer implements Consumer<String, String> {
#Override
public void unsubscribe() {
}
}
#Service
public class MyService {
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscriptions() {
kafkaConsumer.unsubscribe();
}
}
}
#MockBean // this will create a mockito bean and put it in the application context in place of the Kafka consumer bean defined in the TestContext class
private Consumer<String, String> kafkaConsumer;
#Autowired // inject the bean from the application context that is wired with the mock bean
private MyService myService;
#Test
public void testClearSubscriptions() {
myService.clearSubscriptions();
Mockito.verify(kafkaConsumer, Mockito.times(1)).unsubscribe();
}
}