Testowanie konwersacji
Omawiając testy integracyjne pominąłem kwestię konwersacji. Dlatego dzisiaj postanowiłem utworzyć i przetestować nowy komponent, działający w oparciu o konwersację.
Interfejs
Komponent będzie pozwalał na utworzenie nowego konta użytkownika. Jednak oprócz widoku formularza będzie on posiadał widok z pytaniem o potwierdzenie danych:
package pl.info.czerwinski;
import javax.ejb.Local;
@Local
public interface NewUser {
public User getUser();
public String createUser();
public String sendUserData();
public String confirmUser();
public void removeComponent();
}
Akcja createUser będzie tworzyła nową instancję użytkownika i realizowała przekierowanie na widok formularza.
Po podaniu odpowiednich danych, użytkownik wywoła odpowiednim przyciskiem metodę sendUserData, która spowoduje wyświetlenie widoku z prośbą o potwierdzenie informacji.
Na sam koniec, confirmUser utworzy nowe konto użytkownika. Wówczas nastąpi przekierowanie na stronę główną aplikacji.
Komponent
Poniżej przedstawiam przykładową implementację interfejsu NewUser:
package pl.info.czerwinski;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.security.management.IdentityStore;
@Stateful
@Name("newUser")
@Scope(ScopeType.CONVERSATION)
public class NewUserAction implements NewUser {
static final String FORM_VIEW = "/newUserForm.xhtml";
static final String CONFIRM_VIEW = "/newUserConfirm.xhtml";
static final String END_VIEW = "/home.xhtml";
@In private IdentityStore identityStore;
private User user;
public User getUser() {
return user;
}
@Begin
public String createUser() {
user = new User();
return FORM_VIEW;
}
public String sendUserData() {
return CONFIRM_VIEW;
}
@End(beforeRedirect=true)
public String confirmUser() {
identityStore.createUser(
user.getLogin(), user.getPassword());
return END_VIEW;
}
@Remove
public void removeComponent() {
}
}
Najważniejsze, że komponent jest stanowy i znajduje się w kontekście konwersacji.
Testy
Klasa testów będzie się opierać o ten sam schemat, co przy zmianie hasła. Jednak tym razem dodam do niej jedno prywatne pole:
package pl.info.czerwinski;
import org.jboss.seam.mock.SeamTest;
import org.testng.annotations.Test;
public class NewUserTest extends SeamTest {
private String id = null;
}
Identyfikator widoku (id) pozwala określić konwersację, której dotyczy dane żądanie JSF. Dzięki temu kolejne wywołania FacesRequest.run() będzie można logicznie powiązać ze sobą. Wystarczy wywoływać kolejne żądania w następujący sposób:
id = new FacesRequest("/foo.xhtml", id) {
// […]
}.run();
Pierwszy przypadek testowy sprawdza działanie powyższej konwersacji:
- uruchomienie konwersacji (↓ linie 5–10),
- wprowadzenie danych do formularza (↓ linie 19–23),
- wysłanie danych (↓ linie 24–29),
- sprawdzenie poprawności danych (↓ linie 30–38),
- potwierdzenie dodania nowego konta użytkownika (↓ linie 42–47):
@Test
public void testNewUser() throws Exception {
// Click the "New user" link or button:
id = new FacesRequest("/home.xhtml", id) {
@Override
protected void invokeApplication() throws Exception {
assert invokeMethod("#{newUser.createUser}").equals(
NewUserAction.FORM_VIEW) :
"Wrong form view.";
}
@Override
protected void renderResponse() throws Exception {
assert getValue("#{newUser.user}") != null :
"No new user instance created.";
}
}.run();
// Fill in the form and send the data:
id = new FacesRequest(NewUserAction.FORM_VIEW, id) {
@Override
protected void updateModelValues() throws Exception {
setValue("#{newUser.user.login}", "nowy");
setValue("#{newUser.user.password}", "nowy");
}
@Override
protected void invokeApplication() throws Exception {
assert invokeMethod("#{newUser.sendUserData}").equals(
NewUserAction.CONFIRM_VIEW) :
"Wrong confirm view.";
}
@Override
protected void renderResponse() throws Exception {
assert getValue(
"#{newUser.user.login}").equals("nowy") :
"New user login incorrect.";
assert getValue(
"#{newUser.user.password}").equals("nowy") :
"New user password incorrect";
}
}.run();
// Confirm the data:
id = new FacesRequest(NewUserAction.CONFIRM_VIEW, id) {
@Override
protected void invokeApplication() throws Exception {
assert invokeMethod("#{newUser.confirmUser}").equals(
NewUserAction.END_VIEW) :
"End of conversation: wrong redirection view.";
}
}.run();
}
W drugiej metodzie testowej sprawdzam, czy można się zalogować do systemu używając danych nowego użytkownika:
@Test(dependsOnMethods="testNewUser")
public void testLogin() throws Exception {
// Log in:
new FacesRequest("/login.xhtml") {
@Override
protected void updateModelValues() throws Exception {
setValue("#{credentials.username}", "nowy");
setValue("#{credentials.password}", "nowy");
}
@Override
protected void invokeApplication() throws Exception {
invokeMethod("#{identity.login}");
}
@Override
protected void renderResponse() throws Exception {
assert (Boolean) getValue("#{identity.loggedIn}") :
"Not logged in.";
}
}.run();
// Log out:
new FacesRequest("/home.xhtml") {
@Override
protected void invokeApplication() throws Exception {
invokeMethod("#{identity.logout}");
}
@Override
protected void renderResponse() throws Exception {
assert (Boolean) getValue("#{not identity.loggedIn}") :
"Still logged in.";
}
}.run();
}
Podsumowanie
Dzisiejszym artykułem zamykam temat testowania automatycznego aplikacji Seam.
Jest jeszcze sporo zagadnień, których nie omówiłem – między innymi konfiguracja testów w pliku XML czy klasa NonFacesRequest. Być może w przyszłości jeszcze do nich wrócę, lecz w najbliższej przyszłości zajmę się innymi zagadnieniami.



