Como setar dado privado em testes no Spring

Willyan Guimarães
experienceCode
Published in
2 min readFeb 7, 2020

Quando não se está fazendo testes de integração, podem ocorrer cenários que você necessite setar um atributo privado de uma classe a ser testada onde não é possível injetar valor via mock tendo em vista que provavelmente está se usando apenas Mockito + JUnit.

Para exemplificar, pense que temos uma @Service responsável por gerar uma senha com X número de caracteres onde a variável “length” é responsável por inferir o número de caracteres.

@Service
public class SecurityService {

@Value("length_password")
private Integer length;

public String getPassword() {

Random random = new SecureRandom();

IntStream specialChars = random.ints(length, 33, 45);

Stream<Character> characterStream = specialChars.mapToObj(data -> (char) data);

return characterStream.collect(Collector.of(
StringBuilder::new,
StringBuilder::append,
StringBuilder::append,
StringBuilder::toString));
}
}

Se perceber verá que existe nesta variável que informa o tamanho da senha uma externalização de configuração. Isso é uma boa prática no sentido que possibilita mudar configurações da aplicação em relação a seu comportamento sem necessidade de mudança de código.

O problema é que no caso de testar de forma unitária não é possível realizar o mock deste atributo. Veja uma classe de teste e o erro de execução pela variável não possuir valor:

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class SecurityServiceTest {

@InjectMocks
private SecurityService securityService = new SecurityService();

@Test
public void testGetPassword() {
String password = securityService.getPassword();

assertNotNull(password);

int lengthPassword = 6;
assertEquals(6, password.length());
}

}

Execução do Teste:

java.lang.NullPointerException
at com.example.demo.service.SecurityService.getPassword(SecurityService.java:22)
at com.example.demo.service.SecurityServiceTest.testGetPassword(SecurityServiceTest.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Como já era esperado, um NullPointerException ocorre.

Para resolver esta situação, existe a classe ReflectionTestUtils que via reflexão trata estes tipos de cenário durante a execução do teste.

Basta aplicar a seguinte mudança:

@BeforeEach
public void setUp() {
ReflectionTestUtils.setField(securityService, "length", 6);
}

O código completo ficaria:

@ExtendWith(MockitoExtension.class)
public class SecurityServiceTest {

@InjectMocks
private SecurityService securityService = new SecurityService();

@BeforeEach
public void setUp() {
ReflectionTestUtils.setField(securityService, "length", 6);
}

@Test
public void testGetPassword() {
String password = securityService.getPassword();

assertNotNull(password);

int lengthPassword = 6;
assertEquals(6, password.length());
}
}

Pronto! Desta forma a variável na instância utilizada passa a ter o valor inferido possibilitando o execução com sucesso do teste.

Espero ter ajudado!

--

--