Wrzesień
19
2009

Nowy walidator Hibernate

Słowa kluczowe: , , , , | Kategorie: Seam Framework
No Gravatar

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")@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…

Napisz Komentarz

*