Primera experiencia con Mock Objects

Palabras clave:
Tiempo aproximado: 2 min.
Estoy emocionado porque acabo de terminar mi primer test unitario usando mock objects. He comprobado un validador JSF que ha hecho un compañero sin necesidad de ejecutarlo dentro del contenedor web y sin tener que codificar un costoso contexto (que podría, además, incorporar defectos ajenos al sujeto de la prueba -el validador-).

He probado jMock e EasyMock y me he quedado con EasyMock. Os enseño los dos ejemplos:

Con jMock:


import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(JMock.class)
public class PublisherTest {

Mockery context = new JUnit4Mockery();

/**
* A Publisher sends a message to a single registered Subscriber.
*
*/
@Test
public void oneSubscriberReceivesAMessage() {

/*
* We first set up the context in which our test will execute. We create
* a Publisher to test. We create a mock Subscriber that should receive
* the message. We then register the Subscriber with the Publisher.
* Finally we create a message object to publish.
*/

// set up
final Subscriber subscriber = context.mock(Subscriber.class);

Publisher publisher = new Publisher();
publisher.add(subscriber);

final String message = "message";

/*
* Next we define expectations on the mock Subscriber that specify the
* methods that we expect to be called upon it during the test run. We
* expect the receive method to be called once with a single argument,
* the message that will be sent.
*/

// expectations
context.checking(new Expectations() {
{
one(subscriber).receive(message);
}
});

/*
* We then execute the code that we want to test.
*/

// execute
publisher.publish(message);

/*
* After the code under test has finished our test must verify that the
* mock Subscriber was called as expected. If the expected calls were
* not made, the test will fail. The MockObjectTestCase does this
* automatically. You don't have to explicitly verify the mock objects
* in your tests. The JMock test runner does this automatically. You
* don't have to explicitly verify the mock objects in your tests.
*/
// mockery.assertIsSatisfied();

}
}

Con EasyMock:


import org.easymock.EasyMock;
import org.junit.Test;

public class PublisherTest {

private Publisher publisher;
private Subscriber subscriber;

/**
* A Publisher sends a message to a single registered Subscriber.
*
*/
@Test
public void oneSubscriberReceivesAMessage() {

// create the collaborators as mocks
subscriber = EasyMock.createMock(Subscriber.class);

// setup
publisher = new Publisher();
publisher.add(subscriber);
final String message = "message";

// expectations
subscriber.receive(message);
EasyMock.expectLastCall().times(2);

// end setup
EasyMock.replay(subscriber);


// execution
publisher.publish(message);
publisher.publish(message);

// assertion
EasyMock.verify(subscriber);
}

}

Como se puede comprobar, el código del test con EasyMock es más intuitivo. Sólo una salvedad: si queréis hacer “mocks” de clases abstractas necesitaréis también “EasyMock Class Extension” y CGLIB (yo me he descargado cglib-nodep-2.1_3.jar para evitar problemas de dependencias).

Las ventajas de hacer tests con “mocks” son:

  • evitamos costosas “fixtures” (objetos creados ad-hoc como contexto de la prueba) que, además, pueden introducir/ocultar defectos
  • podemos probar el comportamiento de nuestro sujeto (no sólo el estado final)

Esto último es especialmente necesario en el caso de componentes a los que no podamos decirle assertEquals de nada y que debamos comprobar que interactúa con sus colaboradores como se haya definido.