網頁

2019/1/10

使用SpringBoot打造記帳簿專案(十三)撰寫第一支測試程式

本篇會在記帳簿專案中撰寫第一支測試程式。

使用SpringBoot打造記帳簿專案(十一)建立第一個RestController中我們建立了一支HelloController及方法hello()。並在 使用SpringBoot打造記帳簿專案(十二)使用Log4j2 + SLF4J記錄日誌加入了基本的日誌設定。


以下會撰寫一支測試HelloController.hello()的測試程式HelloControllerTest.testHello()

撰寫測試程式前要先將測試框架如JUnitMockito等匯入SpringBoot專案,然而在使用SpringBoot打造記帳簿專案(十)建立SpringBoot專案中建立好SpringBoot的Web專案時已經在pom.xml檔中加入了spring-boot-starter依賴如下,所以不用另外設定就可以直接使用。

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

因為被測試的HelloController的package為idv.matt.controller,所以在src/test/java建立有相同package的idv.matt.controller.HelloControllerTest,如此被測試程式與測試程式會在classpath下的同的package內,測試程式(HelloControllerTest)就可以呼叫被測試程式(HelloController)除private方法以外的其他方法。

HelloController

package idv.matt.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

  static final Logger logger = LoggerFactory.getLogger(HelloController.class.getName());

  @GetMapping("/hello")
  public String hello() {
    logger.info("start");

    logger.info("end");
    return "hello moneynote";
  }

}

HelloControllerTest

package idv.matt.controller;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;

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.junit4.SpringRunner;

import idv.matt.application.MoneynoteApplication;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { MoneynoteApplication.class })
public class HelloControllerTest {

  @Autowired
  HelloController helloController;

  @Test
  public void testHelloReturnExpectedString() {
    final String expected = "hello moneynote";
    final String actual = helloController.hello();
    assertEquals(expected, actual);
  }

  @Test
  public void testHelloReturnUnexceptedString() {
    final String expected = "hello";
    final String actual = helloController.hello();
    assertNotEquals(expected, actual);
  }

}

在SpringBoot的測試類別加上@RunWith(SpringRunner.class),意思就是使用SpringRunner來執行測試程式。

測試方法必須加上@Test才會被JUnit執行。

SpringRunner提供Spring TestContext Framework的功能給JUnit框架,並與@SpringBootTest搭配來讓測試類別可以在Spring的context(運行環境)中執行。

簡單說就是在測試程式中一樣可以利用Spring容器的bean,例如上面範例的helloController的實例就是透過Spring的@Autowired注入。

若把@RunWith(SpringRunner.class)拿掉,則當helloController呼叫hello()時會丟出NullPointerException錯誤,因為若不使用@RunWith明確指出要使用什麼Runner,則JUnit預設會使用BlockJUnit4ClassRunner來執行,但這個Runner不是運行在Spring context中,所以@Autowired沒有效果,無法注入HelloController的實例bean,也就是null,所以在呼叫方法時丟出NullPointerException

若把@SpringBootTest拿掉並執行測試,則會出現NoSuchBeanDefinitionException錯誤如下:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'idv.matt.controller.HelloController' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

原因是@SpringBootTest負責提供Spring context,若拿掉@SpringBootTest則測試類別就不知道有HelloController這個bean的存在。


HelloControllerTest中建立testHelloReturnExpectedString()方法和testHelloReturnUnexceptedString。這兩個方法都是用來測試HelloController.hello()的執行結果是否符合預期,如果符合則通過,若不符合則JUnit會報錯。

將建立好的HelloControllerTest置於Eclipse編輯區,並在上按滑鼠右鍵 -> Run As -> JUnit Test來執行測試。測試執行結果顯示在Eclipse的JUnit視窗。


專案結構目錄如下



接下來會先將專案推送到Github,請見使用SpringBoot打造記帳簿專案(十四)提交至Github


參考:

沒有留言:

張貼留言