Mike Nitchie
Mike Nitchie

Reputation: 1174

Testing Spring Boot MVC with autowired repositories

I have two projects. One called myCore and one called myWeb. myCorecontains all of the beans and repository interfaces. myWeb is simply a JSON api that brings in myCore as a dependency. When I run myWeb everything works fine. When I try to run unit tests against the controllers in myWeb the @Autowired repos are null. I need to find some way for the @Autowired repository interfaces to have the same value as when the application is running, or to mock those objects. Preferably I would like to know how to implement both solutions.

myCore pom.xml:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.1.4.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.mongodb</groupId>
        <artifactId>mongo-java-driver</artifactId>
        <version>2.12.4</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-mongodb</artifactId>
        <version>1.6.1.RELEASE</version>
    </dependency>

myCore context.xml

<mongo:repositories base-package="com.myOrg.myapp.repo.mongo" />

<mongo:db-factory id="dbfactory" host="removed"
    port="removed" dbname="removed" username="removed" password="removed" />

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongoDbFactory" ref="dbfactory" />
</bean>

repo interface:

public interface SiteRepository extends PagingAndSortingRepository<Site, String>{

    void findBySomethingCustom(String somethingCustom);
}

In the web project: 'myWeb` pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <start-class>com.myOrg.myApp.Application</start-class>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>com.myOrg</groupId>
        <artifactId>myCore</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

myWeb config:

@Configuration
@ImportResource("classpath*:/spring/context.xml")
public class Config {}

myWeb controller:

@RestController
@RequestMapping("site")
public class SiteController {

    @Autowired
    private SiteRepository siteRepo;

    @RequestMapping(value = "", method = RequestMethod.GET)
    public Iterable<Site> getSites() {
        return siteRepo.findAll();
    }
}

And finally, the test class:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
@ContextConfiguration(classes = Config.class)
public class SiteControllerTest {
    private MockMvc mvc;

    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(new SiteController()).build();
    }

    @Test
    public void testGetSites() throws Exception {
        mvc.perform(
            MockMvcRequestBuilders.get("/site"));
    }
}

I am getting a null pointer exception in testGetSites() that indicates siteRepo is null. However, navigating to the url of that controller method executes normally.

Again, I either need that to have the value it has when the application runs normally, and/or a way to mock that object so the test can pass(or fail) normally.

UPDATE Changed mvc = MockMvcBuilders.standaloneSetup(new SiteController()).build(); to mvc = MockMvcBuilders.webAppContextSetup(wac).build(); where wac references

@Autowired
private WebApplicationContext wac;

Also changed:

@SpringApplicationConfiguration(classes = {MockServletContext.class}

to

@SpringApplicationConfiguration(classes = {Application.class})

Upvotes: 2

Views: 2678

Answers (1)

sodik
sodik

Reputation: 4683

I believe NPE is cause by mvc = MockMvcBuilders.standaloneSetup(new SiteController()). Javadoc says:

This allows full control over the instantiation and initialization of controllers, and their dependencies, similar to plain unit tests while also making it possible to test one controller at a time.

In my interpretation it means that your are responsible for providing all dependencies (e.g. you can provide mocks etc). And since you have just created new instance without any dependencies, you got NPE.

So I see two solution:

  1. Provide your own mocked dependencies

  2. Use webAppContextSetup.webAppContextSetup instead if you want spring resolve dependecies for you.

Upvotes: 3

Related Questions