Fiz um teste de integração na minha API Spring sem muita ajuda e preciso de feedback!
Olá comunidade!
Depois de 1 ano e meio de experiência com Java e sem auxílio direto ou assistir muitos vídeos (só um de 8 minutos 🫠), consegui implementar um teste de integração na minha API Spring. Eu estou trabalhando em um desafio estilo PicPay simplificado, e o projeto está quase finalizado. Agora estou na fase de escrever testes para garantir que tudo funcione direitinho.
A real é que não sei o quanto do que fiz está certo ou errado. Por isso, queria muito o feedback da comunidade! Vou compartilhar um dos testes que escrevi para validar a lógica de transferência de dinheiro na API. Aqui vai o código:
Controller
@SpringBootTest
@AutoConfigureJsonTesters
@AutoConfigureMockMvc
class ControllerTest {
// Constantes para teste
final BigDecimal AMOUNT_100 = new BigDecimal("100.00");
final BigDecimal AMOUNT_0 = new BigDecimal("0.00");
@Autowired
private TransferMoneyTestScenario testScenario;
@Autowired
private CreateUserTestScenario userTestScenario;
@Autowired
private MockMvc mvc;
@Test
@DisplayName("Merchant cant do it transfer money.")
void should_not_be_possible_merchant_user_transfer_money_to_common_user() throws Exception {
// Given
var commonUserResp = userTestScenario.createCommonUser();
var merchantUserResp = userTestScenario.createMerchantUser();
var commonId = userTestScenario.getIdFromResponse(commonUserResp);
var merchantId = userTestScenario.getIdFromResponse(merchantUserResp);
userTestScenario.updateBalance(commonId);
userTestScenario.updateBalance(merchantId);
testScenario.paymentAllowedByAuthorizer(true);
// When
var response = testScenario.executeTransferMoneyRequest(
AMOUNT_100, merchantId, commonId
);
// Then
var expectedResponse = testScenario
.expectedErrorResponse(
"Bad Request",
"Comerciantes não estão autorizados fazer transferência."
);
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.getContentAsString()).isEqualTo(expectedResponse);
userTestScenario.deleteUserAndWallet(commonId);
userTestScenario.deleteUserAndWallet(merchantId);
}
}
Criei duas classes para abstrair a parte considera "feia" dos testes para não confundir quem está lendo; o Java já é meio verboso, deixar tudo junto deixaria o código bem dificil de ler
TransferMoneyTestScenario
@Component
public class TransferMoneyTestScenario {
@MockBean
private AuthorizationClient authorizationClient;
@Autowired
private MockMvc mvc;
@Autowired
private JacksonTester<TransferMoneyRequest> transferMoneyJson;
public void paymentAllowedByAuthorizer(Boolean value) {
var data = new Data(value);
if(value) {
var authorizedResponse = new Authorize("sucess", data);
Mockito.when(authorizationClient.execute()).thenReturn(authorizedResponse);
}
var authorizedResponse = new Authorize("fail", data);
Mockito.when(authorizationClient.execute()).thenReturn(authorizedResponse);
}
public MockHttpServletResponse executeTransferMoneyRequest(
BigDecimal value, Integer payerId, Integer payeeId) throws Exception
{
var request = new TransferMoneyRequest(value, payerId, payeeId);
return mvc.perform(post("/v1/transfer")
.contentType(MediaType.APPLICATION_JSON)
.content(transferMoneyJson.write(request).getJson()))
.andReturn()
.getResponse();
}
public String expectedErrorResponse(
String reasonPhrase, String expectedMessage) throws JsonProcessingException
{
var errorMessage = new RestErrorMessage(
reasonPhrase,
expectedMessage);
var objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(errorMessage);
}
}
CreateUserTestScenario
@Component
public class CreateUserTestScenario {
@Autowired
private MockMvc mvc;
@Autowired
private JacksonTester<CreateUserData> createUserJson;
@Autowired
private UserRepositoryTest userRepository;
@Autowired
private WalletRepositoryTest walletRepository;
public MockHttpServletResponse createCommonUser() throws Exception {
var request = new CreateUserData("CommonUser",
"019.327.760-36",
"[email protected]",
"123"
);
return mvc.perform(post("/v1/user")
.contentType(MediaType.APPLICATION_JSON)
.content(createUserJson.write(request).getJson()))
.andReturn()
.getResponse();
}
public MockHttpServletResponse createMerchantUser() throws Exception {
var request = new CreateUserData("MerchantUser",
"43.674.835/0001-30",
"[email protected]",
"123"
);
return mvc.perform(post("/v1/user")
.contentType(MediaType.APPLICATION_JSON)
.content(createUserJson.write(request).getJson()))
.andReturn()
.getResponse();
}
public Integer getIdFromResponse(MockHttpServletResponse response) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
ResponseUserData responseData = objectMapper.readValue(response.getContentAsString(), ResponseUserData.class);
return responseData.getCreateUserResponse().id();
}
public void deleteUserAndWallet(Integer id) {
walletRepository.deleteByUserId(id);
userRepository.deleteByUserId(id);
}
public void updateBalance(Integer id) {
walletRepository.setBalance(id);
}
}
Dúvidas
- Arquitetura dos testes: Faz sentido o que fiz com os cenários (CreateUserTestScenario e TransferMoneyTestScenario)?
- Boas práticas: Alguma sugestão para melhorar a clareza ou estrutura do teste?
🎄 Boas festas pra vcs, valeus! 🎄