本篇會在記帳簿專案中撰寫第一支測試程式。
在使用SpringBoot打造記帳簿專案(十一)建立第一個RestController中我們建立了一支HelloController
及方法hello()
。並在
使用SpringBoot打造記帳簿專案(十二)使用Log4j2 + SLF4J記錄日誌加入了基本的日誌設定。
以下會撰寫一支測試HelloController.hello()
的測試程式HelloControllerTest.testHello()
。
撰寫測試程式前要先將測試框架如JUnit,Mockito等匯入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中,所以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。
參考:
沒有留言:
張貼留言