Este post é a parte final da nossa série de posts, você pode conferir os primeiros nos links: parte 1 parte 2. Nos primeiros posts criamos uma API seguindo o padrão REST utilizando Kotlin, Spring Webflux e MongoDB. Porém, nossa API não pode ser considerada pronta pois falta algo muito importante e, muitas vezes, negligenciado pelos programadores: Documentação e testes!!

Documentação

Vamos utilizar o swagger (https://swagger.io/) para criar uma documentação de forma simples e elegante. Para começar precisamos trazer as dependências do swagger para o nosso projeto no build.gradle.kts

implementation("io.springfox:springfox-swagger2:2.9.2")
implementation("io.springfox:springfox-swagger-ui:2.9.2")

A primeira linha é referente a documentação (JSON) e a segunda é referente a interface gráfica.

No package config (o mesmo que utilizamos para configurar o Mongo) vamos criar a classe SpringFoxConfig

@Configuration
@EnableSwagger2
class SwaggerConfig {

    @Bean
    fun api(): Docket = Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.any())
            .build()

}

Pronto! O Swagger Spring Fox é totalmente integrado ao Spring. Para acessar a documentação do swagger basta rodar o projeto e acessar o endereço http://localhost:8080/v2/api-docs e teremos como resultado um JSON contento toda a documentação da nossa API. Felizmente, ao trazermos a dependência do swagger-ui temos acesso a interface gráfica no endereço http://localhost:8080/swagger-ui.html.

Aqui temos a disposição um documentação completa e intuitiva da nossa api, inclusive temos possibilidade de testar nossos endpoints:

Muito legal né? Temos que tomar alguns cuidados pois, muitas vezes, não queremos disponibilizar acesso ao “try-out” da nossa API sem passar pelo front-end (mas isso é assunto para outro post).

Testes

Por fim chegamos aos testes. É aqui que muitos programados batem cabeça ou POs decidem “a gente faz isso depois”. Poderia citar diversas vantagens mas, para mim, a principal é a segurança na hora de fazer qualquer manutenção no código. Eu vejo como “antiquada” aquelas documentações em papel, dizendo o que o programa faz (ou deveria fazer). Os testes são uma ótima forma de documentar as regras e garantir que nenhuma implementação futura venha a quebrar estas regras de forma acidental.

Testes Unitários

Neste post vamos falar apenas de testes unitários pois os testes integrados serão assunto de um post futuro. No teste unitário nós vamos testar de forma isolada a menor parte do nosso código, no caso testaremos métodos. Mas como vamos tratar as nossas “dependências” de outras classes ou do banco de dados? A resposta é simples: Mocks!! Tradução: objetos que simulam as caracteristicas e comportamentos dos objetos de forma controlada.

Vamos acrescentar a dependência da biblioteca Mockk (https://mockk.io/). No mundo Java estávamos acostumados a utilizar o Mockito, porém o Mockk foi criado pensando na syntaxe e nas funções existentes no Kotlin.

testImplementation("io.mockk:mockk:1.9.3")

Vamos testar a classe GameService na qual ficam as regras de negócio, mais especificamente o método de criação de games. Dentro da pasta test / kotlin / io.redspark.games vamos criar a classe GameServiceUnitTest e criar o método createGameTest com a anotação @Test.

class GameServiceUnitTests {

    @Test
    fun createGameTest() {  
    }

}

A implementação GameServiceImpl tem como dependência a classe (interface) GameRepository, então vamos ter que criar nosso primeiro mock:

val gameRepository = mockk<GameRepository>()

Precisamos mockar todos os métodos que serão utilizados na hora de criar um game:

val game = GameEntity(
             id = "123", 
             title = "Candy Crush", 
             releaseDate = LocalDate.of(2012, 4, 12))
every { gameRepository.save(any<GameEntity>()) } returns Mono.just(game)

Aqui estamos dizendo que toda (every) chamada passando qualquer (any) gameEntity no método save do mock gameRepository retornará (returns) uma instância de game encapusulada em Mono (reactor).

Podemos instanciar nosso service passando a classe que acabamos de mockar:

val gameService = GameServiceImpl(gameRepository)

e aí é só fazer a chamada do método que queremos testar:

val dto = CreateGameDTO(title = "Candy Crush", releaseDate = LocalDate.of(2012, 4, 12))
val monoCreatedGame = gameService.createGame(dto)

Existem diversas formas de validar um objeto encapsulado em um fluxo reativo (Mono), vamos utilizar o método block para o teste unitário:

val createdGame = monoCreatedGame.block()
Assertions.assertEquals("123", createdGame?.id)
Assertions.assertEquals("Candy Crush", createdGame?.title)
Assertions.assertEquals(LocalDate.of(2012, 4, 12), createdGame?.releaseDate)

Por hora é isso, espero que tenham gostado. Vocês podem conferiro código fonte completo em https://github.com/fabiotadashi/redspark-kotlin-api.