viernes, 16 de noviembre de 2018

Como simular o mockear atributos y clases estáticas

Hace poco me preguntarón como hacerlo y no supe responder correctamente al tema, igual es algo antiguo pero la verdad es que yo no lo sabía. Por tanto hoy hablaremos de como mockear un atributo  o clase estática. 

Para ello nos apoyaremos en PowerMock, otro framework que nos permitirá simular nuestras clases y hacer unos test JUnit más eficientes. Este está compuesto de varias APIs para otros frameworks de simulación, como EasyMock o Mockito, y otra que nos facilitará las tareas de reflexión. En esta otra API nos centraremos para llevar a cabo el post.

Teniendo la siguiente clase con dos métodos, uno que tira de una clase estática y otro de un atributo estático, veremos como probar ambos.

public class BookController {
 private static BookWSClient client = new BookWSClient();
 public Book getBookByStaticAttribute(final Integer id) {
  return client.getBook(id);
 }
 public Book getBookByStaticClass(final Integer id) {
  return BookWSHelper.getBook(id);
 }
}

Para el primer caso haremos uso de la clase 'Whitebox' que es la base de la API de reflección. Con ella podremos acceder a atributos, métodos y constructores privados. Para el caso que nos corresponde, un atributo privado, usaremos el método 'setInternalState'

@RunWith(PowerMockRunner.class)
public class WhiteboxExample {
 @Mock
 private BookWSClient client;
 private BookController controller;
 @Test
 public void getBookByStaticAttribute() throws RemoteException {
  Mockito.when(client.getBook(1)).thenReturn(new Book("name", "author"));
  Book book = controller.getBookByStaticAttribute(1);
  assertEquals("name", book.getName());
  Mockito.verify(client, Mockito.times(1)).getBook(1);
 }
 @Before
 public void setUp() throws Exception {
  Whitebox.setInternalState(BookController.class, "client", client);
  controller = new BookController();
 }
}

En el segundo haremos uso de la anotación 'PrepareForTest' que nos permitirá mockear clases finales o clases con métodos finales, privados o estáticos. Y luego tendremos que usar el método 'mockStatic' para indicar que deseamos 'mockear' la clase estática de la que haremos uso.

@RunWith(PowerMockRunner.class)
@PrepareForTest(BookWSHelper.class) // class with static method
public class MockStatic {
 private BookController controller;
 @Test
 public void getBookByStaticClass() throws RemoteException {
  controller = new BookController();
  PowerMockito.mockStatic(BookWSHelper.class);
  Mockito.when(BookWSHelper.getBook(1)).thenReturn(new Book("name", "author"));
  Book book = controller.getBookByStaticClass(1);
  assertEquals("name", book.getName());
 }
}

1 comentario: