播放路线/控制器仅用于单元测试

时间:2015-10-27 17:45:57

标签: java unit-testing playframework

我有一个方法可以播放Http.Context和"做一些事情"随着会议。我想为这个方法编写一个单元测试。具体来说,我想测试一下,如果请求带有某些标头我的方法正常工作。似乎最可靠的方法是为我的测试创建FakeApplicationController。然后,我使用Helpers.fakeRequest来获取请求,并Helpers.route将该请求路由到我的控制器。控制器会调用我的方法,设置一些变量等等,然后我可以断言成功等等。

似乎是一个精彩的计划,但我无法弄清楚如何在FakeApplication中向我的控制器添加路线。请注意,此控制器并非真正属于我的应用程序 - 它只是我想要用于此一项测试的内容。所以我想定义它并在这个单元测试中构建;我不想将其添加到我的conf/routes文件中。

具体来说,我想要这样的事情:

// Maybe I can use GlobalSettings.onRouteRequest but the return type
// is play.api.mvc.Handler which seems inaccessible from Java
FakeApplication app = Helpers.fakeApplication(new MyGlobalSettings());
Http.Request request = Helpers.fakeRequest().withCookies(...).withBody(...);
Controller testContoller = new MyTestController();
// This doesn't exist, but I want something like this
app.addRoute("/foo", ctx -> testController.method(ctx));
running(app, () -> {
    Helpers.route("/foo");
    assertThat(testContoller.itWorked()).isTrue();
}

我正在运行Play 2.2.3并使用Java编写,而不是Scala。

我意识到我可以直接构造Http.Context并将其传递给我的方法。但是,出于以下几个原因,这不是我的首选方法:

  • Http.Context构造函数采用会话变量的纯文本。我希望在请求包含加密会话cookie时测试一些正常工作。
  • Http.Context构造函数记录很少,看起来有些偏差。例如,您可以将Http.Request传递给构造函数,但也可以传递cookie数据和会话数据。那么请求中的cookie /会话数据会发生什么?它是否与传递的其他数据合并?忽略?
  • Http.Context构造函数很难从Java中使用,因为它需要play.api.mvc.RequestHeader,它不能用Java构建,而play.mvc.Http.Request也不能用FakeRequest有用的""由Java构建(您可以构建一个,但不包含cookie,标题等,Http.Request无法转换为Http.Context
  • 感觉更多"黑盒"发送请求并确保工作正常而不是试图弄清楚这个特定版本的Play如何将我的请求转换为return MyStuffObjs.Select(item => (MyStuff)item.Clone()).ToArray(); (例如,手动构建上下文似乎更有可能打破新版本的游戏)。 / LI>

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

以格式播放测试

running(fakeApplication(), () -> {
  ...
});

适用于在没有HTTP层的情况下测试正在运行的播放应用。但是在你的情况下,你依赖于一个http上下文,所以我可以选择添加http层......

running(testServer(3333), fakeApplication(), () -> {
   WSResponse wsResponse = WS.url("http://localhost:3333/foo").setHeader("fizz", "buzz").get().get(30, TimeUnit.SECONDS);
  ....
  //assert some stuff
});

或者尝试使用PowerMockito并模拟HTTP.Context调用。正如你所指出的那样,这更加脆弱,但可以实用地进行快速单元测试。像

这样的东西
@RunWith(PowerMockRunner.class)
public class FooTest {

    @PrepareForTest({ Http.Context.class })
    @Test
    public void test() {
        mockStatic(Http.Context.class)
        mockStatic(Http.class)

        Http.Context mockContext = mock(Http.Context.class);
        Map<String, String> args new HashMap<>();
        args.put("a","b");
        mockContext.args = args;
        PowerMockito.when(Http.Context.current()).thenReturn(mockContext);

        ClassUnderTest cut = new ClassUnderTest();
        cut.someMethod();
        //assertions

    }

}

答案 1 :(得分:0)

@Before
public void startPlay() {
    String conf = System.getProperty("config.file");
    if (conf == null) {
        System.setProperty("config.file", "../../conf/test.conf");
    }
    System.setProperty("play.http.router", "customer.Routes");
    super.startPlay();
}

答案 2 :(得分:-1)

这是您在不更改conf/routes文件的情况下添加其他测试路线的方法。但MyTestController.java需要位于project / app而不是project / test。

// app/controllers/TestController.java
public static Result foo() {
   return ok();
}

// conf/test.routes
GET     /foo           controllers.TestController.foo

// test/controllers/TestTestController.java
@Before 
Configuration config = new Configuration(ConfigFactory.parseFile(new File("conf/test.conf")).resolve());
Map<String, Object> configMap = config.asMap();
Map<String, Object> application = (Map<String, Object>) config.get("application");
application.put("router", "test.Routes");
configMap.put("application", application);
fakeApplication(configMap);

@Test
FakeRequest fakeRequest = Helpers.fakeRequest("GET", "/foo");
Result result = Helpers.route(fakeRequest);
assertThat(Helpers.status(result)).isEqualTo(200);