生活随笔
收集整理的這篇文章主要介紹了
在Spring Boot项目中使用Spock框架
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
轉(zhuǎn)載:https://www.jianshu.com/p/f1e354d382cd
Spock框架是基于Groovy語(yǔ)言的測(cè)試框架,Groovy與Java具備良好的互操作性,因此可以在Spring Boot項(xiàng)目中使用該框架寫優(yōu)雅、高效以及DSL化的測(cè)試用例。Spock通過(guò)@RunWith注解與JUnit框架協(xié)同使用,另外,Spock也可以和Mockito(Spring Boot應(yīng)用的測(cè)試——Mockito)協(xié)同使用。
在這個(gè)小節(jié)中我們會(huì)利用Spock、Mockito一起編寫一些測(cè)試用例(包括對(duì)Controller的測(cè)試和對(duì)Repository的測(cè)試),感受下Spock的使用。
How Do
- 根據(jù)Building an Application with Spring Boot這篇文章的描述,spring-boot-maven-plugin這個(gè)插件同時(shí)也支持在Spring Boot框架中使用Groovy語(yǔ)言。
- 在pom文件中添加Spock框架的依賴
- 在src/test目錄下創(chuàng)建groovy文件夾,在groovy文件夾下創(chuàng)建com/test/bookpub包。
- 在resources目錄下添加packt-books.sql文件,內(nèi)容如下所示:
INSERT INTO author (id, first_name, last_name) VALUES (5, 'Shrikrishna', 'Holla'); INSERT INTO book (isbn, title, author, publisher) VALUES ('978-1-78398-478-7', 'Orchestrating Docker', 5, 1); INSERT INTO author (id, first_name, last_name) VALUES (6, 'du', 'qi'); INSERT INTO book (isbn, title, author, publisher) VALUES ('978-1-78528-415-1', 'Spring Boot Recipes', 6, 1); - 在com/test/bookpub目錄下創(chuàng)建SpockBookRepositorySpecification.groovy文件,內(nèi)容是:
package com.test.bookpubimport com.test.bookpub.domain.Authorimport com.test.bookpub.domain.Book
import com.test.bookpub.domain.Publisher
import com.test.bookpub.repository.BookRepository import com.test.bookpub.repository.PublisherRepository import org.mockito.Mockito import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.SpringApplicationContextLoader import org.springframework.context.ConfigurableApplicationContext import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.web.WebAppConfiguration import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.setup.MockMvcBuilders import spock.lang.Sharedimport spock.lang.Specification import javax.sql.DataSourceimport javax.transaction.Transactional import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebAppConfiguration @ContextConfiguration(classes = [BookPubApplication.class, TestMockBeansConfig.class],loader = SpringApplicationContextLoader.class) class SpockBookRepositorySpecification extends Specification { @Autowired private ConfigurableApplicationContext context; @Shared boolean sharedSetupDone = false; @Autowired private DataSource ds; @Autowired private BookRepository bookRepository; @Autowired private PublisherRepository publisherRepository; @Shared private MockMvc mockMvc; void setup() { if (!sharedSetupDone) { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); sharedSetupDone = true; } ResourceDatabasePopulator populator = new ResourceDatabasePopulator(context.getResource("classpath:/packt-books.sql")); DatabasePopulatorUtils.execute(populator, ds); } @Transactional def "Test RESTful GET"() { when: def result = mockMvc.perform(get("/books/${isbn}")); then: result.andExpect(status().isOk()) result.andExpect(content().string(containsString(title))); where: isbn | title "978-1-78398-478-7"|"Orchestrating Docker" "978-1-78528-415-1"|"Spring Boot Recipes" } @Transactional def "Insert another book"() { setup: def existingBook = bookRepository.findBookByIsbn("978-1-78528-415-1") def newBook = new Book("978-1-12345-678-9", "Some Future Book", existingBook.getAuthor(), existingBook.getPublisher()) expect: bookRepository.count() == 3 when: def savedBook = bookRepository.save(newBook) then: bookRepository.count() == 4 savedBook.id > -1 } } - 執(zhí)行測(cè)試用例,測(cè)試通過(guò)
- 接下來(lái)試驗(yàn)下Spock如何與mock對(duì)象一起工作,之前的文章中我們已經(jīng)在TestMockBeansConfig類中定義了PublisherRepository的Spring Bean,如下所示,由于@Primary的存在,使得在運(yùn)行測(cè)試用例時(shí)Spring Boot優(yōu)先使用Mockito框架模擬出的實(shí)例。
@Configuration
@UsedForTesting
public class TestMockBeansConfig { @Bean @Primary public PublisherRepository createMockPublisherRepository() { return Mockito.mock(PublisherRepository.class); } } - 在BookController.java中添加getBooksByPublisher接口,代碼如下所示:
@Autowired
public PublisherRepository publisherRepository;@RequestMapping(value = "/publisher/{id}", method = RequestMethod.GET) public List<Book> getBooksByPublisher(@PathVariable("id") Long id) { Publisher publisher = publisherRepository.findOne(id); Assert.notNull(publisher); return publisher.getBooks(); } - 在SpockBookRepositorySpecification.groovy文件中添加對(duì)應(yīng)的測(cè)試用例,
def "Test RESTful GET books by publisher"() { setup: Publisher publisher = new Publisher("Strange Books") publisher.setId(999) Book book = new Book("978-1-98765-432-1", "Mytery Book", new Author("Jhon", "Done"), publisher) publisher.setBooks([book]) Mockito.when(publisherRepository.count()). thenReturn(1L); Mockito.when(publisherRepository.findOne(1L)). thenReturn(publisher) when: def result = mockMvc.perform(get("/books/publisher/1")) then: result.andExpect(status().isOk()) result.andExpect(content().string(containsString("Strange Books"))) cleanup: Mockito.reset(publisherRepository) } - 運(yùn)行測(cè)試用例,發(fā)現(xiàn)可以測(cè)試通過(guò),在控制器將對(duì)象轉(zhuǎn)換成JSON字符串裝入HTTP響應(yīng)體時(shí),依賴Jackson庫(kù)執(zhí)行轉(zhuǎn)換,可能會(huì)有循環(huán)依賴的問(wèn)題——在模型關(guān)系中,一本書依賴一個(gè)出版社,一個(gè)出版社有包含多本書,在執(zhí)行轉(zhuǎn)換時(shí),如果不進(jìn)行特殊處理,就會(huì)循環(huán)解析。我們這里通過(guò)@JsonBackReference注解阻止循環(huán)依賴。
分析
可以看出,通過(guò)Spock框架可以寫出優(yōu)雅而強(qiáng)大的測(cè)試代碼。
首先看SpockBookRepositorySpecification.groovy文件,該類繼承自Specification類,告訴JUnit這個(gè)類是測(cè)試類。查看Specification類的源碼,可以發(fā)現(xiàn)它被@RunWith(Sputnik.class)注解修飾,這個(gè)注解是連接Spock與JUnit的橋梁。除了引導(dǎo)JUnit,Specification類還提供了很多測(cè)試方法和mocking支持。
Note:關(guān)于Spock的文檔見(jiàn)這里:Spock Framework Reference Documentation
根據(jù)《單元測(cè)試的藝術(shù)》一書中提到的,單元測(cè)試包括:準(zhǔn)備測(cè)試數(shù)據(jù)、執(zhí)行待測(cè)試方法、判斷執(zhí)行結(jié)果三個(gè)步驟。Spock通過(guò)setup、expect、when和then等標(biāo)簽將這些步驟放在一個(gè)測(cè)試用例中。
- setup:這個(gè)塊用于定義變量、準(zhǔn)備測(cè)試數(shù)據(jù)、構(gòu)建mock對(duì)象等;
- expect:一般跟在setup塊后使用,包含一些assert語(yǔ)句,檢查在setup塊中準(zhǔn)備好的測(cè)試環(huán)境
- when:在這個(gè)塊中調(diào)用要測(cè)試的方法;
- then : 一般跟在when后使用,盡可以包含斷言語(yǔ)句、異常檢查語(yǔ)句等等,用于檢查要測(cè)試的方法執(zhí)行后結(jié)果是否符合預(yù)期;
- cleanup:用于清除setup塊中對(duì)環(huán)境做的修改,即將當(dāng)前測(cè)試用例中的修改回滾,在這個(gè)例子中我們對(duì)publisherRepository對(duì)象執(zhí)行重置操作。
Spock也提供了setup()和cleanup()方法,執(zhí)行一些給所有測(cè)試用例使用的準(zhǔn)備和清除動(dòng)作,例如在這個(gè)例子中我們使用setup方法:(1)mock出web運(yùn)行環(huán)境,可以接受http請(qǐng)求;(2)加載packt-books.sql文件,導(dǎo)入預(yù)定義的測(cè)試數(shù)據(jù)。web環(huán)境只需要Mock一次,因此使用sharedSetupDone這個(gè)標(biāo)志來(lái)控制。
通過(guò)@Transactional注解可以實(shí)現(xiàn)事務(wù)操作,如果某個(gè)方法被該注解修飾,則與之相關(guān)的setup()方法、cleanup()方法都被定義在一個(gè)事務(wù)內(nèi)執(zhí)行操作:要么全部成功、要么回滾到初始狀態(tài)。我們依靠這個(gè)方法保證數(shù)據(jù)庫(kù)的整潔,也避免了每次輸入相同的數(shù)據(jù)。
作者:杜琪
鏈接:https://www.jianshu.com/p/f1e354d382cd
來(lái)源:簡(jiǎn)書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
轉(zhuǎn)載于:https://www.cnblogs.com/ceshi2016/p/8442470.html
總結(jié)
以上是生活随笔為你收集整理的在Spring Boot项目中使用Spock框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。