Skip to content

testing strategies

Shelson Ferrari edited this page Jul 23, 2024 · 5 revisions

Testing Strategies

The project follows a comprehensive testing strategy that includes unit tests, integration tests, and end-to-end (E2E) tests to ensure the robustness and reliability of the application.

Unit Tests

Unit tests are used to test individual components of the application in isolation. The goal is to verify that each component functions as expected.

Example Unit Test

The following is an example of a unit test for the CurrencyConversionService class:

package com.shelson.application.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import com.shelson.application.dto.CurrencyConversionDTO;
import com.shelson.domain.model.Currency;
import com.shelson.domain.model.CurrencyConversion;
import com.shelson.domain.repository.CurrencyConversionRepository;
import com.shelson.infrastructure.exception.BusinessException;
import com.shelson.infrastructure.exception.ResourceNotFoundException;

@ExtendWith(MockitoExtension.class)
public class CurrencyConversionServiceTest {

    private static final Logger logger = LoggerFactory.getLogger(CurrencyConversionServiceTest.class);

    @Mock
    private RestTemplate restTemplate;

    @Mock
    private CurrencyConversionRepository repository;

    @InjectMocks
    private CurrencyConversionService service;

    @Test
    public void testConvertCurrency() throws BusinessException {
        logger.info("Iniciando teste para conversão de moeda com dados válidos");

        Currency source = Currency.USD;
        Currency target = Currency.EUR;
        double rate = 0.85;
        double amount = 100.0;
        LocalDateTime now = LocalDateTime.now();

        CurrencyConversionService.ApiResponse mockResponse = new CurrencyConversionService.ApiResponse();
        Map<String, Double> rates = new HashMap<>();
        rates.put(target.getCode(), rate);
        mockResponse.setRates(rates);
        when(restTemplate.getForObject(anyString(), eq(CurrencyConversionService.ApiResponse.class))).thenReturn(mockResponse);
        logger.debug("Mock response from API: {}", mockResponse);

        CurrencyConversion savedConversion = new CurrencyConversion(source, target, rate, now);
        when(repository.save(any(CurrencyConversion.class))).thenReturn(savedConversion);
        logger.debug("Mock saving to repository: {}", savedConversion);

        CurrencyConversionDTO resultDTO = service.convertCurrency(source, target, amount);
        logger.info("Resultado da conversão: {}", resultDTO);

        assertThat(resultDTO.getSourceCurrency()).isEqualTo(source);
        assertThat(resultDTO.getTargetCurrency()).isEqualTo(target);
        assertThat(resultDTO.getConversionRate()).isEqualTo(rate);

        LocalDateTime expectedQueryDate = now.withNano(0);
        LocalDateTime actualQueryDate = resultDTO.getQueryDate().withNano(0);
        assertThat(actualQueryDate).isEqualTo(expectedQueryDate);
        
        verify(repository).save(any(CurrencyConversion.class));
        logger.info("Teste para conversão de moeda com dados válidos concluído com sucesso");
    }

    @Test
    public void testConvertCurrencyWithNullSource() {
        logger.info("Iniciando teste para conversão de moeda com moeda de origem nula");
        assertThatThrownBy(() -> service.convertCurrency(null, Currency.EUR, 100.0))
            .isInstanceOf(BusinessException.class)
            .hasMessageContaining("Source and target currencies must not be null");
        logger.info("Teste para conversão de moeda com moeda de origem nula concluído");
    }

    @Test
    public void testConvertCurrencyWithNullTarget() {
        logger.info("Iniciando teste para conversão de moeda com moeda de destino nula");
        assertThatThrownBy(() -> service.convertCurrency(Currency.USD, null, 100.0))
            .isInstanceOf(BusinessException.class)
            .hasMessageContaining("Source and target currencies must not be null");
        logger.info("Teste para conversão de moeda com moeda de destino nula concluído");
    }

    @Test
    public void testConvertCurrencyWithInvalidCurrency() {
        logger.info("Iniciando teste para conversão de moeda com moeda inválida");
        assertThatThrownBy(() -> service.convertCurrency(Currency.USD, Currency.valueOf("INVALID"), 100.0))
            .isInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("No enum constant com.shelson.domain.model.Currency.INVALID");
        logger.info("Teste para conversão de moeda com moeda inválida concluído");
    }

    @Test
    public void testConvertCurrencyWithApiError() {
        logger.info("Iniciando teste para conversão de moeda com erro da API");
        when(restTemplate.getForObject(anyString(), eq(CurrencyConversionService.ApiResponse.class)))
            .thenThrow(HttpClientErrorException.class);

        assertThatThrownBy(() -> service.convertCurrency(Currency.USD, Currency.EUR, 100.0))
            .isInstanceOf(ResourceNotFoundException.class)
            .hasMessageContaining("Error fetching exchange rates from API");
        logger.info("Teste para conversão de moeda com erro da API concluído");
    }
}

Integration Tests

Integration tests are used to test the interactions between different components of the application. They ensure that the components work together as expected.

End-to-End (E2E) Tests

E2E tests simulate real user scenarios and test the application from start to finish. They help verify that the entire system works as expected.

Conclusion

By combining unit tests, integration tests, and E2E tests, we can ensure the robustness and reliability of the application. Each type of test plays a crucial role in the overall testing strategy, helping to identify and fix issues at different stages of development.


Wiki Menu

Wiki Main Page

1. Introduction to the Project

  • Overview: Presentation of the project, highlighting its purpose and the context in which it is embedded.
  • Project Objectives: Enumeration of the main objectives that the project aims to achieve.
  • Scope and Functionalities: Description of the main functionalities offered by the project and its scope of operation.

2. Configuration and Installation

3. Project Structure

  • Folder Structure: Description of the organization of the project directories.
  • Project Architecture: Explanation of the architecture used, including design patterns and technical decisions.

4. Development

  • Development Flow: Description of the development process adopted, including planning, coding, and review stages.
  • Apache Camel Integration: Guide on integrating Apache Camel into the project, including configuration and usage.
  • Contributors and Authors: Recognition of the contributors to the project.
  • Contributions: Guidelines on how to contribute to the project, including code standards and pull request requirements, tips and best practices.
  • Code of Conduct: Behavioral guidelines expected for the project community.

5. API and Documentation

6. Endpoints and Database

  • Endpoint Description: Details of the available API endpoints, including methods, parameters, and usage examples.
  • Database Management: Strategies and practices for efficient management of the database used by the project.

7. Testing

  • Testing Strategies.
  • Testing Tools: Description of the testing tools used in the project and how to configure them.

8. CI/CD and Automations

  • CI/CD Pipeline: Explanation of the continuous integration and delivery pipeline, detailing each stage and its function.
  • Automations and Artifact Generation: Description of the automations incorporated into the CI/CD, including documentation generation and build artifacts.

9. Configuration Files

10. Best Practices

11. Legal and Licensing

  • Licensing: Information about the rights and restrictions associated with the use of the software.
  • Terms of Use: Information about the terms and conditions for using the software.

12. Projections and Innovations

  • Future Plans: Discussion on functionalities and improvements considered for future versions of the project.
  • Improvement Proposals: Space for the community to suggest and debate improvements and innovations.

13. Attachments and Useful Links

14. Security

  • Security Policy: Details on the supported versions, reporting vulnerabilities, and general security practices.

Clone this wiki locally