Nowy walidator Hibernate
Testy jednostkowe w Seam Framework nie różnią się od innych testów jednostkowych. Jednak to od nich powinienem zacząć.
Do tego celu postanowiłem wykorzystać nowy walidator Hibernate, sprawdzający poprawność nazwy użytkownika (loginu).
Adnotacja
Najpierw utworzę nową adnotację @ValidLogin, która na równi z @NotNull czy @Email będzie wymuszała sprawdzanie poprawności danych ustawianych w encjach. Cały plik ValidLogin.java będzie wyglądał następująco:
package pl.info.czerwinski;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hibernate.validator.ValidatorClass;
@ValidatorClass(LoginValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidLogin {
String message() default "Nieprawidłowy login";
}
Bardzo istotna jest adnotacja @ValidatorClass (linia 10), określająca faktyczną klasę walidatora dla pól i właściwości oznaczonych przez @ValidLogin.
„Pusty” walidator
Obiekt klasy LoginValidator będzie na początku uznawać każdą wartość za poprawną:
package pl.info.czerwinski;
import org.hibernate.validator.Validator;
public class LoginValidator
implements Validator<ValidLogin> {
public void initialize(ValidLogin annotation) {
}
public boolean isValid(Object value) {
return true;
}
}
Każdy walidator implementuje dwie metody interfejsu Validator:
initialize- pozwala pobrać ewentualne dodatkowe parametry adnotacji walidującej (np.
@Length(min=3)), isValid- sprawdza poprawność podanej wartości.
Testy
Testowanie za pomocą biblioteki TestNG opisałem w jednym z wcześniejszych artykułów. Dlatego pozwolę sobie na pominięcie szczegółów.
Klasa testowa będzie wyglądać mniej więcej tak:
package pl.info.czerwinski;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class LoginValidatorTest {
private LoginValidator validator;
@BeforeClass
public void setUp() {
validator = new LoginValidator();
}
@DataProvider(name="loginValidator.isValid")
public Object[][] getIsValidData() {
return new Object[][] {
{"admin", true},
{"Admin", true},
{"admin1", true},
{"admin_1", true},
{"admin 1", false},
{"1", false},
{new Integer(1), false},
{new Double(1.0), false},
};
}
@Test(dataProvider="loginValidator.isValid")
public void testIsValid(Object value, boolean valid) {
assert validator.isValid(value) == valid :
"Validation result is wrong.";
}
}
Walidator jest obiektem bezstanowym, więc wystarczy, że utworzę go tylko raz (@BeforeClass). Metoda testowa testIsValid będzie sprawdzać działanie walidacji dla szeregu zestawów parametrów, pobieranych z getIsValidData (dzięki @Test(dataProvider="loginValidator.isValid") i @DataProvider(name="loginValidator.isValid")).
Sprawdzenie poprawności walidacji (linie 32–33) odbywa się poprzez porównanie wartości oczekiwanej (valid) z wynikiem działania metody isValid(value). Słowo kluczowe assert sprawi, że przy niespełnionym warunku validator.isValid(value) == valid zostanie zgłoszony wyjątek AssertionError z podanym opisem ("Validation result is wrong.").
Aby testy zadziałały, należy jeszcze dodać przynajmniej jeden plik *Test.xml. Musi się on znajdować gdzieś wewnątrz katalogu src/test/, aby został uwzględniony przy wykonywaniu polecenia ant test. Ja utworzyłem plik src/test/pl/info/czerwinski/BlogTest.xml:
<?xml version="1.0" encoding="UTF-8"?>
<suite name="Blog" verbose="1">
<test name="Validators">
<classes>
<class name="pl.info.czerwinski.LoginValidatorTest"/>
</classes>
</test>
</suite>
Plik zawiera informacje o zestawie testów zawierającym jedną klasę – LoginValidatorTest.
Wykonanie testów daje następujący rezultat:
slawek@localhost:~/java/blog$ ant test […] [testng] =============================================== [testng] Blog [testng] Total tests run: 8, Failures: 4, Skips: 0 [testng] ===============================================
4 spośród 8 testów nie powiodły się. Nic dziwnego – metoda isValid zawsze zwraca wartość true, a nie powinna.
Poprawienie walidatora
Aby testy zakończyły się pełnym sukcesem, należy poprawić metodę LoginValidator.isValid:
public boolean isValid(Object value) {
if (value instanceof String) {
if (java.util.regex.Pattern.matches(
"^[a-zA-Z][!-~]*$", value.toString())) {
return true;
}
}
return false;
}
Jeszcze raz uruchamiam testy:
slawek@localhost:~/java/blog$ ant test […] [testng] =============================================== [testng] Blog [testng] Total tests run: 8, Failures: 0, Skips: 0 [testng] ===============================================
Wszystko działa, jak należy.
Zachęcam do dalszych eksperymentów z danymi testowymi – może jednak wystąpią jakieś nieprzewidziane błędy w działaniu walidatora…



