Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't Autowire JpaRepository in Junit test - Spring boot application

Hi All

I’m trying to execute a Junit test in a Spring boot application, the Junit should test some CRUD operations, I’m using Spring Repositories specifically JpaRepository.

The Repository calss:

package au.com.bla.bla.bla.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import au.com.bla.bla.bla.entity.Todo;

public interface TodoRepository extends JpaRepository<Todo, Integer> {

}

TodoController class

package au.com.bla.bla.bla.controller;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import au.com.bla.bla.bla.entity.Todo;
import au.com.bla.bla.bla.repository.TodoRepository;


import java.util.List;
import java.util.Map;


@RestController
@CrossOrigin
public class TodoController
{
    static final String TEXT = "text";

    @Autowired
    private TodoRepository todoRepository;

...
    @RequestMapping(path = "/todo", method = RequestMethod.POST)
    public Todo create(@RequestBody Map<String, Object> body)
    {
        Todo todo = new Todo();
        todo.setCompleted(Boolean.FALSE);
        todo.setText(body.get(TEXT).toString());
        todoRepository.save(todo);
        return todo;
    }
...

The JUnit:

package au.com.bla.bla.bla.controller;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
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.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import au.com.bla.bla.bla.repository.TodoRepository;

@WebMvcTest(TodoController.class)
@RunWith(SpringRunner.class)
public class TodoControllerTest {

  @Autowired
  private MockMvc mvc;

  @Autowired
  private TodoController subject;

  @Before
 public void setUp() {

  }

  @Test
  public void testCreate() throws Exception {

    String json = "{\"text\":\"a new todo\"}";

    mvc.perform(post("/todo").content(json)
                             .contentType(APPLICATION_JSON)
                             .accept(APPLICATION_JSON))
       .andExpect(status().isOk())
       .andExpect(jsonPath("$.id").value(3))
       .andExpect(jsonPath("$.text").value("a new todo"))
       .andExpect(jsonPath("$.completed").value(false));

    assertThat(subject.getTodos()).hasSize(4);
  }
  ...

Problem:

When executing the Junit I end up with this exception:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'todoController': Unsatisfied dependency expressed through field 'todoRepository': No qualifying bean of type [au.com.bla.bla.bla.repository.TodoRepository] found for dependency [au.com.bla.bla.bla.repository.TodoRepository]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [au.com.bla.bla.bla.repository.TodoRepository] found for dependency [au.com.bla.bla.bla.repository.TodoRepository]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
…

Can anyone help with this ? Thanks in advance

like image 626
Greg Avatar asked Oct 15 '16 22:10

Greg


People also ask

How do I enable JpaRepository in spring boot?

To activate the Spring JPA repository support, we can use the @EnableJpaRepositories annotation and specify the package that contains the DAO interfaces: @EnableJpaRepositories(basePackages = "com.

Can you Autowire in a jUnit?

Also note that we can wire other spring beans in our jUnit test classes using @Autowired annotation.

Which is better CrudRepository or JpaRepository?

Crud Repository doesn't provide methods for implementing pagination and sorting. JpaRepository ties your repositories to the JPA persistence technology so it should be avoided. We should use CrudRepository or PagingAndSortingRepository depending on whether you need sorting and paging or not.


1 Answers

Your error is actually the expected behavior of @WebMvcTest. You basically have 2 options to perform tests on your controller.

1. @WebMvcTest - need to use @MockBean

With @WebMvcTest, a minimal spring context is loaded, just enough to test the web controllers. This means that your Repository isn't available for injection:

Spring documentation:

@WebMvcTest will auto-configure the Spring MVC infrastructure and limit scanned beans to @Controller, @ControllerAdvice, @JsonComponent, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver.

Assuming the goal is just to test the Controller, you should inject your repository as a mock using @MockBean.

You could have something like:

@RunWith(SpringRunner.class)
@WebMvcTest(TodoController.class)
public class TodoControllerTest {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private TodoController subject;

    @MockBean
    private TodoRepository todoRepository;

    @Test
    public void testCreate() throws Exception {
    String json = "{\"text\":\"a new todo\"}";

    mvc.perform(post("/todo").content(json)
                             .contentType(APPLICATION_JSON)
                             .accept(APPLICATION_JSON))
       .andExpect(status().isOk())
       .andExpect(jsonPath("$.id").value(3))
       .andExpect(jsonPath("$.text").value("a new todo"))
       .andExpect(jsonPath("$.completed").value(false));

        Mockito.verify(todoRepository, Mockito.times(1)).save(any(Todo.class));
    }
}

2. @SpringBootTest - you can @Autowire beans

If you want to load the whole application context, then use @SpringBootTest: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html

You'd have something like this:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class TodoControllerTest {

    private MockMvc mvc;

    @Autowired
    TodoController subject;

    @Autowired
    private WebApplicationContext context;

    @Before
    public void setup() {
        this.mvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    @Test
    public void testNoErrorSanityCheck() throws Exception {
        String json = "{\"text\":\"a new todo\"}";

        mvc.perform(post("/todo").content(json)
                .contentType(APPLICATION_JSON)
                .accept(APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id").value(3))
        .andExpect(jsonPath("$.text").value("a new todo"))
        .andExpect(jsonPath("$.completed").value(false));

        assertThat(subject.getTodos()).hasSize(4);
    }
}
like image 190
alexbt Avatar answered Sep 22 '22 00:09

alexbt