Some of posts from this blog has been moved to dywicki.pl. You will be automatically redirected to new blog if you would submit comment.
New posts are published on dywicki.pl, this blog contains old content and it is not continued.

Niektóre posty z tego bloga zostały przeniesione do dywicki.pl. Zostaniesz automatycznie przekierowany jeśli bedzięsz chciał dodać komentarz.
Nowe posty sa publikowane na dywicki.pl, ten blog zawiera stare treści i nie jest kontynuowany.

Testowanie tablic argumentów z EasyMock

Filed under Java,Porady,Testy jednostkowe by

Często zdarza się że metody, które piszemy i później testujemy mają argumenty w postaci tablic. EasyMock wówczas potrafi zgłosić wyjątek, że przekazana tablica jest różna od oczekiwanej mimo, że zawartość tablic jest identyczna.

java.lang.AssertionError: 
  Unexpected method call find([Ljava.lang.String;@1ad77a7):
    find([Ljava.lang.String;@b8f82d): expected: 1, actual: 0
package org.code_house.test.mock;

import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;

import java.util.Collections;
import java.util.List;

import junit.framework.TestCase;

interface Finder {
    List<String> find(String[] names);
}

public class FinderTest extends TestCase {

    public void testFind() {
        Finder mock = createMock(Finder.class);
        expect(mock.find(aryEq(new String[] {"Amy", "Luke"}))).andReturn(
            Collections.EMPTY_LIST);
        replay(mock);

        List<String> list = mock.find(new String[] {"Amy", "Luke"});
        assertTrue(list.isEmpty());
    }
}

Rozwiązaniem tych problemów jest użycie mechanizmu “matcherów”, które są wbudowane w sam szkielet. Najprostszym sposobem na ich użycie jest odwołanie przy pomocy statycznego wywołania którejś z metod EasyMock. Poniżej znajduje się lista dostępnych matcherów. Wartość “given” to argument przekazany w wywołaniu metody.

  • eq(X value)
    Argument pasuje jeśli wyrażenie value.equals(given) zwróci true. Dostępny dla wszystkich typów prostych i obiektów.
  • anyBoolean(), anyByte(), anyChar(), anyDouble(), anyFloat(), anyInt(), anyLong(), anyObject(), anyShort()
    Dowolna wartość zostanie przepuszczona niezależnie od tego co będzie przekazane. Dostępny dla wszystkich typów.
  • eq(X value, X delta)
    Pasuje jeśli aktualna jeśli wyrażenie given jest równe value z dokładnością do delta. Dostępne dla typów float oraz double.
  • aryEq(X value)
    Pasuje jeśli wyrażenie Arrays.equals(value, given) zwraca true. Dostępne dla tablic typów prostych jak i dla obiektów.
  • isNull()
    Pasuje jeśli given == null. Dostępne tylko dla obiektów.
  • notNull()
    Pasuje jeśli given != null. Dostępne tylko dla obiektów.
  • same(X value)
    Pasuje jeśli wyrażenie value == given jest prawdziwe. Dostępne tylko dla obiektów.
  • isA(Class clazz)
    Pasuje jeśli given instanceof clazz zwraca true. Given może być instancję clazz jak i pochodną.
  • lt(X value), leq(X value), geq(X value), gt(X value)
    Zwraca true jeśli given jest mniejsze, mniejsze równe, większe równe bądź większe niż value. Dostępne dla wszystkich numerycznych typów prostych.
  • startsWith(String prefix), contains(String substring), endsWith(String suffix)
    Zwraca true jeśli given zaczyna się, zawiera bądź kończy się daną wartością. Dostępne dla argumentów typu String.
  • matches(String regex), find(String regex)
    Pasuje jeśli given pasuje w całości do wyrażenia/fragment pasuje. Dostępne dla argumentów typu String.
  • and(X first, X second)
    Zwraca true jeśli matcher first oraz second zwracają prawdę.
  • or(X first, X second)
    Zwraca true jeśli matcher first bądź second pasuje.
  • not(X value)
    Zwraca true jeśli matcher value zwrócił fałsz.

Uzbrojeni w taki zestaw matcherów możemy poprawić kod testu tak by był on poprawny:


public class FinderTest extends TestCase {

    public void testFind() {
        Finder mock = createMock(Finder.class);
        expect(mock.find(aryEq(new String[] {"Amy", "Luke"}))).andReturn(
            Collections.EMPTY_LIST);
        replay(mock);

        List<String> list = mock.find(new String[] {"Amy", "Luke"});
        assertTrue(list.isEmpty());
    }
}

Inny przykład:

public class FinderTest extends TestCase {

    public void testFind() {
        Finder mock = createMock(Finder.class);
        expect(mock.find("Lucy", aryEq(new String[] {"Amy", "Luke"}))).andReturn(
            Collections.EMPTY_LIST);
        replay(mock);

        List<String> list = mock.find("Lucy", new String[] {"Amy", "Luke"});
        assertTrue(list.isEmpty());
    }
}

Warto zwrócić uwagę na to, że jeśli korzystamy dla matchera aby dopasować argument to musimy zrobić to dla wszystkich, inaczej poleci wyjątek:

java.lang.IllegalStateException: 2 matchers expected, 1 recorded.

Stąd nasz test w końcowej postaci powinien wyglądać następująco:

package org.code_house.test.mock;

import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;

import java.util.Collections;
import java.util.List;

import junit.framework.TestCase;

interface Finder {
    List<String> find(String[] names);

    List<String> find(String name, String[] names);
}

public class FinderTest extends TestCase {

    public void testFind() {
        Finder mock = createMock(Finder.class);
        expect(mock.find(eq("Lucy"), aryEq(new String[] {"Amy", "Luke"}))).andReturn(
            Collections.EMPTY_LIST);
        replay(mock);

        List<String> list = mock.find("Lucy", new String[] {"Amy", "Luke"});
        assertTrue(list.isEmpty());
    }
}

No responses yet

Leave a Reply