2013-11-15 22 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放在一起并编译。如果您想测试传入的内容,则需要在调用视图之前拦截它。

通过移动所有的业务逻辑从控制器的解决这个问题,你将不得不重写你的控制器,只有有必要的请求处理代码那里。这几乎会更容易测试。

相关问题