Nov 26 2008
Testowanie tablic argumentów z EasyMock
Filed under Java, Porady, Testy jednostkowe by Łukasz Dywicki
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());
}
}