1. 程式人生 > 實用技巧 >5.5使用Cucumber來測試

5.5使用Cucumber來測試

這一節,我們將繼續前面的例子,然後不加Cucumber-JVM實現,這個將需要以java為基礎的最初Ruby Cucumber框架的版本,並且建立一些測試來說明我們的應用和觀點。

5.5.1程式碼實現

  1. 我們需要在build.gradle的檔案中加入如下的包依賴:

testCompile("info.cukes:cucumber-spring:1.2.2")
testCompile("info.cukes:cucumber-java8:1.2.2")
testCompile("info.cukes:cucumber-junit:1.2.2")

  1. 接著,我們需要建立測試驅動去執行Cucumber測試類。讓我們建立一個RunCukeTest.java的檔案,放置到src/test/java/org/owen/bookpub目錄下。

@RunWith(Cucumber.class)

@CucumberOptions(plugin = { "pretty", "html:build/reports/cucumber" }, glue = {

"cucumber.api.spring", "classpath:org.test.bookpub" }, monochrome =

true)

public class RunCukeTests

{

}

  1. 接下來,我們需要用到Cucumber涉及到的步驟宣告。我人先建立RepositoryStepefs.java的檔案放置到src/test/java/org/owen/bookpub目錄下。

@WebAppConfiguration

@ContextConfiguration(classes = {BookPubApplication.class,

TestMockBeansConfig.class }, loader = SpringApplicationContextLoader.class)

public class RepositoryStepdefs

{

@Autowired

private WebApplicationContext context;

@Autowired

private DataSource ds;

@Autowired

private BookRepository bookRepository;

private Book loadedBook;

@Given("^([^\\\"]*) fixture is loaded$")

public void data_fixture_is_loaded(String fixtureName) throws Throwable

{

ResourceDatabasePopulator populator = new ResourceDatabasePopulator(

context.getResource("classpath:/" + fixtureName + ".sql"));

DatabasePopulatorUtils.execute(populator, ds);

}

@Given("^(\\d+) books available in the catalogue$")

public void books_available_in_the_catalogue(int bookCount)

throws Throwable

{

assertEquals(bookCount, bookRepository.count());

}

@When("^searching for book by isbn ([\\d-]+)$")

public void searching_for_book_by_isbn(String isbn) throws Throwable

{

loadedBook = bookRepository.findBookByIsbn(isbn);

assertNotNull(loadedBook);

assertEquals(isbn, loadedBook.getIsbn());

}

@Then("^book title will be ([^\"]*)$")

public void book_title_will_be(String bookTitle) throws Throwable

{

assertNotNull(loadedBook);

assertEquals(bookTitle, loadedBook.getTitle());

}

}

  1. 現在,我們需要建立響應測試特徵的宣告檔案,檔名為repositories.feature在src/test/resource/org/owen/bookpub目錄下。

@txn
Feature: Finding a book by ISBN
Background: Preload DB Mock Data
Given packt-books fixture is loaded
Scenario: Load one book
Given 3 books available in the catalogue
When searching for book by isbn 978-1-78398-478-7
Then book title will be Orchestrating Docker

  1. 最後,我們需要建立packt-books.sql檔案在src/test/resources目錄下。

INSERT INTO author (id, first_name, last_name) VALUES (5,
'Shrikrishna', 'Holla')
INSERT INTO book (isbn, title, author_id, publisher_id) VALUES ('978-1-
78398-478-7', 'Orchestrating Docker', 5, 1)

  1. 執行gradle test。
  2. 執行好了之後,我們也可以獲取到Cucumber特殊的測試報告檔案,該檔案位於build/reports/tests/index.html.開啟這個html檔案,我們將會看到如下資訊:

  1. 選擇Scenario:Load one book,將會連線到詳細的頁面。

  1. Cucumber也會建立自己的報告,報告路徑在build/reports/cucumber/index.html
  2. 行為驅動測試不僅僅可以建立個別的情況,也可以宣告完整大綱,也就是定義個引數供全域性檔案中的這個名稱的使用。讓我們在src/test/resource/org/owen/bookpub目錄下建立restful.feature的檔案,檔案內容如下:

@txn
Feature: Finding a book via REST API
Background:
Given packt-books fixture is loaded
Scenario Outline: Using RESTful API to lookup books by ISBN
Given catalogue with books
When requesting url /books/<isbn>
Then status code will be 200
And response content contains <title>
Examples:
|isbn |title |
|978-1-78398-478-7|Orchestrating Docker|
|978-1-78528-415-1|Spring Boot Recipes |

  1. 我們還需要在src/test/java/org/owen/bookpub目錄下建立RestfulStepdefs.java檔案。

import java.io.IOException;

import org.owen.bookpub.repository.BookRepository;

import org.springframework.beans.factory.annotation.Autowired;

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.ResultActions;

import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import org.springframework.web.context.WebApplicationContext;

import cucumber.api.java.Before;

import cucumber.api.java.en.Given;

import cucumber.api.java.en.Then;

import cucumber.api.java.en.When;

import static org.hamcrest.CoreMatchers.containsString;

import static org.junit.Assert.assertTrue;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;

@WebAppConfiguration

@ContextConfiguration(classes = { BookPubApplication.class,

TestMockBeansConfig.class }, loader = SpringApplicationContextLoader.class)

public class RestfulStepdefs

{

@Autowired

private WebApplicationContext context;

@Autowired

private BookRepository bookRepository;

private MockMvc mockMvc;

private ResultActions result;

@Before

public void setup() throws IOException

{

mockMvc = MockMvcBuilders.webAppContextSetup(context).build();

}

@Given("^catalogue with books$")

public void catalogue_with_books()

{

assertTrue(bookRepository.count() > 0);

}

@When("^requesting url ([^\"]*)$")

public void requesting_url(String url) throws Exception

{

result = mockMvc.perform(get(url));

}

@Then("^status code will be ([\\d]*)$")

public void status_code_will_be(int code) throws Throwable

{

result.andExpect(status().is(code));

}

@Then("^response content contains ([^\"]*)$")

public void response_content_contains(String content) throws Throwable

{

result.andExpect(content().string(containsString(content)));

}

}

  1. 最後,執行測試檔案

5.5.2程式碼說明

讓我們回顧一下Sep Definitions。因為Cucumber框架使用Gherkin塑造檔案為了去描述業務規則及可以被測試,這些描述都是基於英語的句子,句子需要轉換為執行的程式碼。這就是Step Definition的工作。每一步都是一個明確的情節並且需要匹配Step Definition類中的方法,然後被執行。這些匹配通過宣告的一系列表達的步驟註解,如@Given,@When或@Then。正則表示式是匹配一個組,Cucumber使用正則表示式可以精確定位到要執行的方法。

在RepositoryStepdefs中,我們可以看到如下的方法:

@Given("^([^\\\"]*) fixture is loaded$")
public void data_fixture_is_loaded(String fixtureName) {...}

這個@Given註釋包含一個正則表示式匹配repositories.feature檔案中的Given packt-books fixture is loaded這一行資訊,之後packt-books資訊就要作為fixtureName的引數。@When 和 @Then 的註解原理也是一樣的。總而言之,Cucumber框架就是匹配來自我們定義的feature的檔案,然後將英語的單詞作為類的執行方法的對應引數。

我們已經瞭解了Cucumber基礎知識,讓我們來學習一下如何通過配置來讓測試整合Spring Boot.

這些所有的工作都是來自於RunCukeTests類。這個類並沒有包含任何的測試類,但是有兩份個重要的註解:@RunWith(Cucumber.class)和@CucumberOptons。

  1. @RunWith(Cucumber.class):這個是JUnit註解,宣告JUnit執行需要使用Cucumber Feature檔案去執行測試。
  2. @CucumberOptions:這個提供額外的配置給Cucumber.
  • plugin={"pretty”, “html:build/reports/cucumber”}:這個宣告是讓Cucumber產生html形式的報告,並放入到build/reports/cucumber目錄下。
  • glue={“cucumber.api.spring”, “classpath:org.test.bookup”}:為上一人非常重要的配置,它告訴Cucumber哪個包需要載入和從哪載入測試類。cucumber.api.spring包需要宣告為了cucumber和spring包的整合,org.test.bookpub是我們Step Definition實現的類所在處。
  • monochrome = true:這個是告訴Cucumber不需要帶有ANSI顏色列印輸出,因為我們整合了JUnit。

現在讓我們來看一下RepositoryStepdefs類。

  1. @WebAppConfiguration告訴Spring這個類需要WebApplicationContext初始化,在執行過得,它將會用於測試目的。
  2. @ContextConfiguration(classes = {BookPubApplication.class,
    TestMockBeansConfig.class}, loader =
    SpringApplicationContextLoader.class)宣告去使用BookPubApplication和TestMockBeansConfig類,並將其加入Spring應用的上下文中。當然,使用來自SpringApplicationContextoader類為了去載入程式測試驅動。

因為Cucumber-Spring整合並不知道關於Spring Boot,但是僅僅知道關於Spring,我們不能使用@SpringApplicationConfiguration註解。我們必須使用來自於Spring的適合註解來彌補這個缺陷。我們通過配置類和載入到@ContextConfiguration來解決問題。

一旦我們註解了這個配置,Spring和Spring Boot將會提供給我們方便的自動載入bean用於我們的Step Definition類中。

Cucumber測試可以固定一個新的Step Definition類的實現給每一次測試執行。這個方法命名全域性的,我們可以用這個方法在任何已宣告的Step Definition類中;這些類的操作是各自的,不會影響到其它的。在每一次測試時,由於一個新的實現被建立,宣告的類是有狀態的,並且依賴於內部變數去保持從一個斷言到另外一個斷言的瞬間切換。例如,@When 註解的方法,一個特殊的狀態將會放入;@Then註解,一系列的斷言將會獲取值。在我們的RepositoryStepdefs類的例子中,searching_for_book_by_isbn(…)方法中,我們通過查詢會等到一個變數loadedBook的類,這個類之後會給後面的book_will_be方法使用。在這樣的情況下,如果我們從混合規則中宣告不同的類在我們的feature檔案中,這些類之間是不能互相引用,即不相影響。

在我們整合了Spring後,我們可以解決不同的類之間不能互相呼叫的問題。我們可以看到PublisherRepositoryTests這個之間我們建立的測試類。這個類中有能力可以將@Given註解方法中的特殊行為的mock給其它的測試類,也就是說可以注入或例項到@Then方法註解的方法中。

另外我們聲明瞭一個RestfulStepdefs的類,這裡我們注入了BookRepository類。然而,在restful.feature檔案中,Given packt-books fixture is load的宣告將會轉換到ReposioryStepdefs類的data_fixture_is_loaded的方法中。但是這個兩個類會分享著相同的例項後BookRepositoty物件,並且插入packt-books.sql到資料庫中。

另外一個特徵,我們使用了@txn的註解。這個是告訴Spring在執行測試之間要去擦除,重置資料,隨後要清楚資料庫狀態。