суббота, 5 февраля 2011 г.

EasyMock + JUnit 4 с использованием аннотаций

"Всем хорош EasyMock, но уж больно много писать приходится", - именно с такой мыслью я начал разрабатывать решение, которое позволило бы использовать все преимущества EasyMock и при этом обойтись минимумом строчек кода.




Рассмотрим, для примера, простой код JUnit 4 теста с использованием EasyMock в стандартном стиле.


Мы имеем следующую структуру классов:


SomeObject.java

public class SomeObject {
private Dependency dependency;

public void setDependency(Dependency dependency) {
this.dependency = dependency;
}

public String callToDependency() {
return dependency.callFromSomeObject();
}
}

Dependency.java

public class Dependency {
public String callFromSomeObject() {
return "I'm here";
}
}



Тест для нее выглядел бы следующим образом:

public class SomeObjectTest {
public Dependency dependency;
public SomeObject someObject;

@Before
public void setUp() {
someObject = new SomeObject();
dependency = EasyMock.createMock(Dependency.class);
someObject.setDependency(dependency);
}

@Test
public testThatItMocksDependency() {
expect(dependency.callFromSomeObject()).andReturn("Hello");

EasyMock.replay(dependency);
testThat(someObject.callToDependency(), equalTo("Hello"));
EasyMock.verify(dependency);
}
}




Возможно, если поискать в интернете, то уже можно найти решение, которое бы позволило решить эту проблему, но в силу своей природной лени, а так же духа первооткрывателя, я искать ничего не стал... :)

Результатом стал следующий плод инженерной мысли:



@RunWith(EasyMockRunner.class)
public class SomeObjectTest {
@Mock public Dependency dependency;
@TestObject public SomeObject someObject;

@Test
public testThatItMocksDependency() {
expect(dependency.callFromSomeObject()).andReturn("Hello");

CurrentMocks.replay();
testThat(someObject.callToDependency(), equalTo("Hello"));
}
}


Теперь как собственно это работает:


  • EasyMockRunner собирает все поля теста, которые содержат аннотацию @Mock

  • EasyMockRunner находит тестовый объект помеченный аннотацией @TestObject

  • EasyMockRunner сопоставляет property setter's для @TestObject с @Mock полями (по типу, либо для аннотации @Mock можно напрямую указать название свойства)

  • Перед вызовом тестового метода, создается тестовый объект с использованием конструктора по умолчанию (так же может быть использована аннотация @MockedMethods для тестового метода, в этом случае @TestObject будет создан как partial mock)

  • После этого, для каждого поля с аннотацией @Mock создается mocked object и выставляется для тестового объекта и для самого теста

  • В служебный класс CurrentMocks сохраняется коллекция mocked objects, для того, чтобы можно было вызвать replay, после того как будет записан сценарий для mocked objects (сохраняется в ThreadLocal переменную)

  • В самом тесте, после записи сценария, вызывается CurrentMocks.replay()

  • В EasyMockRunner, после окончания выполнения метода, вызывается EasyMock.verify для всех mocked objects



В следующей серии я подробней расскажу (и покажу!) про классы, которые реализуют этот механизм.

1 комментарий:

  1. Замечательный пост. У тебя талант. Продолжай еще, пожалуйста.

    ОтветитьУдалить