2013-11-15 41 views
2

在我們的遊戲應用中的每個控制器功能從數據庫(或其他方式)獲取數據參數測試,並將這些數值結果如何執行對控制器傳遞給在遊戲框架視圖

def index = Action { implicit request => 
    val newsItems: List[String] = fetchNewsFromDB() 
    Ok(views.html.home.index(newsItems)) 
    } 

def fetchNewsFromDB() = List("Headline1", "Headline2") 

我正在使用說明書編寫測試(基於文檔http://www.playframework.com/documentation/2.2.x/ScalaTest

根據控制器的此文檔,如下所述。在接下來的測試中,我想確保索引頁面包含標題。我通過檢查,如果存在與類「標題」

"Example Page#index" should { 
    "should contain a headline" in { 
     val controller = new TestController() 
     val result: Future[SimpleResult] = controller.index().apply(FakeRequest()) 
     val bodyText: String = contentAsString(result) 
     bodyText.toLowerCase must contain("<div class=\"headline\"") 
    } 
    } 

但是我寧願檢查了控制器傳遞給視圖列表newsItems是否不爲空,一個div做到這一點。

這樣做的最好方法是什麼? 對於需要對控制器進行少量修改的通用方法,是否可以這樣做?

回答

3

我也很沮喪,我無法攔截參數到模板的路上 - 事實上,如果您有大量的測試,甚至可能變得極其困難,甚至無法讓模板在所有中渲染你的頁面中的「狀態」(例如,提供用戶對象,導航助手等的含義)。

最後我做一個額外的「縫」在我的控制器可測性是把什麼;在我的測試中,我將擴展爲被測試的控制器,用一個模擬的替換HTML渲染函數,然後我可以使用它來驗證參數。

下面是一個基於你的「新聞」行動的簡單例子;首先,控制器,它不再是一個object所以我們可以擴展它:

object Application extends ApplicationController 

trait ApplicationController extends Controller { 

    def newsAction = Action { 
    Ok(renderNews("this is the news")) 
    } 

    def renderNews(s:List[String]):Html = html.sandbox(s) 
} 

的方法爲我們提供了所有重要的「測試縫」。我認爲它實際上也提高了控制方法的可讀性也一樣,這是很好的:-)

現在,單元測試:

class ApplicationSpec extends Specification with Mockito { 

    val mockedNewsRenderer = mock[List[String] => Html] 

    val controller = new ApplicationController { 
    override def renderNews(s:List[String]) = mockedNewsRenderer(s) 
    } 

    "Application News Controller" should { 

    "Pass a non-empty list of news items to the template" in { 
     val result = controller.newsAction(FakeRequest()) 

     status(result) must beEqualTo(200) 

     val captor = ArgumentCaptor.forClass(classOf[List[String]]) 

     there was one(mockedNewsRenderer).apply(captor.capture()) 
     val theArgument = captor.getValue 
     theArgument.isEmpty must beFalse 
    } 
    } 
} 

我們創建一個模擬站立式的功能,延長控制器,以便我們可以替換它(注意,我們當然不會更改其他任何內容),然後按照正常的方式調用該操作。需要注意的是,我們仍然可以得到一個標準的播放Result所以我們還是可以檢查狀態碼等,但後來,我們就可以使用Mockito verify functionality that's built into Specs2,連同Mockito's ArgumentCaptor facility斷言,我們的模板確實叫,而且它是用非空列表提供的字符串。

這種做法一直運作良好,我 - 它能夠讓你的控制器,提供高速運行和易於編寫單元測試的真正的好代碼覆蓋。

+0

這種方法遠非理想,因爲每個控制器都需要修改,但我認爲這是一個很好的工作 – Wellingr

+0

正如在我的例子中,'newsItems'的類型是'List [String]' 。是否也可以使用'val mockedNewsRenderer = mock [List [String] => Html]'並檢查作爲參數傳遞的'newsItems'是否非空? – Wellingr

+0

@Fritsie絕對 - 對不起,我沒有注意到你正在使用'List [String]' - 查看我的更新答案,以瞭解如何使用Mockito的'ArgumentCaptor'來斷言模仿協作者的參數 – millhouse

0

你有一個很好的問題,並在測試控制器非常正確的觀點,但我恐怕不能輕易完成。問題是,視圖編譯爲Scala函數,意思是當您調用views.html.home.index(newsItems)時,它將返回一個對象Html,它已經將Html放在一起並編譯。如果您想測試傳入的內容,則需要在調用視圖之前攔截它。

通過移動所有的業務邏輯從控制器的解決這個問題,你將不得不重寫你的控制器,只有有必要的請求處理代碼那裏。這幾乎會更容易測試。

相關問題