Playframework Защищенный модуль: как вы "входите", чтобы протестировать защищенный контроллер в FunctionalTest?
EDIT: я использую Play! версия 1.2 (выпуск продукции)
Я хочу проверить действия контроллера, защищенные модулем Secure
класс, поэтому мне нужно войти в систему до тестирования моего контроллера (иначе я буду перенаправлен на страницу входа).
Я попытался войти в систему до вызова защищенного действия. Вот как выглядит мой FunctionalTest:
@Test
public void someTestOfASecuredAction() {
Map<String, String> loginUserParams = new HashMap<String, String>();
loginUserParams.put("username", "admin");
loginUserParams.put("password", "admin");
// Login here so the following request will be authenticated:
Response response = POST("/login", loginUserParams);
// The following is an action that requires an authenticated user:
Map<String, String> params;
params.put("someparam", "somevalue");
response = POST("/some/secured/action", params);
assertIsOk(response); // this always fails because it is a 302 redirecting to /login
}
Пройдя через код, я подтвердил, что почта входа работает - это вызывает ответ перенаправления с местоположением, установленным на домашней странице (что указывает на успешный вход в систему).
Но затем в следующем вызове защищенного действия я всегда перенаправляюсь на
"/login" - это означает, что мой предыдущий логин не был прикреплен ко второму запросу POST.
Взглянув в исходный код FunctionalTest, я увидел, что был перехватчик @Before, который очищает все файлы cookie. Я попытался переопределить этот intercepter в моем собственном промежуточном суперклассе (чтобы сохранить файлы cookie), но это тоже не сработало.
РЕДАКТИРОВАТЬ: я сбивал с толку play.mvc.Before перед перехватчиком с org.junit. Прежде - первый для использования с Play! контроллеров, последний для тестов JUnit. @Before в FuncitonTest является перехватчиком JUnit, поэтому он должен иметь какое-либо влияние на файлы cookie (поскольку он запускается один раз до запуска теста).
Я не хочу писать тест Selenium для каждого защищенного действия - так как почти все будут защищены. Есть ли способ "обмануть" модуль Secure, полагая, что вы прошли проверку подлинности? Или, может быть, какой-то другой очень очевидный способ справиться с этим (казалось бы, общим) сценарием в FunctionalTest?
Спасибо заранее,
Марк
EDIT: рабочий код, ответ Codemwnci помечен как правильный
Ответ Codemwnci правильный. Вот мой способ обхода файлов cookie с одного запроса на следующий:
@Test
public void someTestOfASecuredAction() {
Map<String, String> loginUserParams = new HashMap<String, String>();
loginUserParams.put("username", "admin");
loginUserParams.put("password", "admin");
Response loginResponse = POST("/login", loginUserParams);
Request request = newRequest();
request.cookies = loginResponse.cookies; // this makes the request authenticated
request.url = "/some/secured/action";
request.method = "POST";
request.params.put("someparam", "somevalue");
Response response = makeRequest(request);
assertIsOk(response); // Passes!
}
Ответы
Ответ 1
Я думаю, что должно быть непонимание того, что делает перехватчик @Before. Он выполняется до выполнения вашего теста. Если ваш тест затем регистрирует вас, то это событие происходит ПОСЛЕ того, как код @Before был выполнен, а результаты защищенного модуля должны быть сохранены в Cookie.
Поэтому я могу только предположить, что Cookie не отправляется со следующим запросом, поэтому я бы предложил попробовать следующее...
получить cookie, используемый безопасным файлом cookie, из объекта Response сразу после вашего входа. Создайте объект Request и установите Cookie в объект запроса, затем вызовите свой метод POST, передав объект запроса.
Я не тестировал этот код, поэтому не уверен, как он будет реагировать на смешение предварительно созданного объекта запроса и передачу URL-адреса, но не уверен, что еще предложить.
Ответ 2
У меня была та же проблема, но в тестах с Play 2.0.4.
Я решил проблему, выполнив следующие Seb и Codemwnci, и я построил, проверив API, следующее решение:
@Test
public void listSomething() {
running(fakeApplication(inMemoryDatabase()), new Runnable() {
@Override
public void run() {
// LOGIN
final Map<String, String> data = new HashMap<String, String>();
data.put("email", "[email protected]");
data.put("password", "userpassword");
Result result = callAction(
controllers.routes.ref.Application.authenticate(),
fakeRequest().withFormUrlEncodedBody(data));
// RECOVER COOKIE FROM LOGIN RESULT
final Cookie playSession = play.test.Helpers.cookie("PLAY_SESSION",
result);
// LIST SOMETHING (using cookie of the login result)
result = callAction(controllers.routes.ref.Application.list(),
fakeRequest().withCookies(playSession));
/*
* WAS RECEIVING 'SEE_OTHER' (303)
* BEFORE RECOVERING PLAY_SESSION COOKIE (BECAUSE NOT LOGGED IN).
*
* NOW, EXPECTED 'OK'
*/
assertThat(status(result)).isEqualTo(OK);
assertThat(contentAsString(result)).contains(
"Something found");
}
});
}
Application.list() выглядит примерно так:
@Security.Authenticated(Secured.class)
public static Result list() {
return ok(list.render(...));
}
Ответ 3
Возможно, вы видите https://bugs.launchpad.net/play/+bug/497408
Я думаю, что он должен работать.
Обновление: я немного отлаживал. Я думаю, что есть две ошибки.
- В FunctionalTest файлы cookie просто отбрасываются, если у них нет maxAge.
- В Scope.java в сеансе поле "TS" устанавливается только в том случае, если ранее существовал файл cookie. Поэтому при первом запуске cookie сеанса он игнорируется при отправке обратно на сервер. Если вы делаете 3 запроса, кажется, что они работают нормально между запросами 2 и 3, но не между запросами 1 и 2, потому что по запросу 2 отправленный cookie не имеет метки времени.
Итак, ошибка 1 разбивает его, если вы не устанавливаете maxAge, а ошибка 2 прерывает его, если вы устанавливаете maxAge.
Обновление 2: я сделал несколько исправлений, которые исправили это для меня: http://play.lighthouseapp.com/projects/57987-play-framework/tickets/775
Ответ 4
Хотите указать, что в окончательном решении Mark S есть ошибка.
Это неверно: request.url = "/some/secured/action";
Это правильно: request.path = "/some/secured/action";
"url" должен быть полным URL-адресом.