Skip to content

McNullty/spring-hal-browser-learning-test

Repository files navigation

Learning test for HAL browser

Abstract

This project shows how to make beautiful REST API with documentation and quick testing with HAL Browser.

Spring Data REST HAL Browser

Spring package spring-data-rest-hal-browser comes with lot of things out of the box, but there are some crucial things that are missing.

Problems with Spring Data REST and Spring Data REST HAL Browser:

  1. Spring Data REST HAL Browser works only with Spring Data REST.

  2. Spring Data REST has problem with POST-ing invalid data. Instead of returning Bed Request HTTP status it returns Internal error (HTTP Status 500).

  3. Spring Data REST also doesn’t support extending autogenerated documentation with custom controllers.

When adding up all problems Spring Data REST and Spring Data REST HAL Browser become unusable in real world projects, but they do make good reference when looklin how to make usable REST API with HAL links.

Explanation of shortcuts

HAL Browser

Better way

HAL Browser should be outside of a project and provided by Docker image. That way you can target localhost while developing REST API for quick check, and then later you can target your project in separate docker container.

Project should link to API documentation created with Spring REST Docs library.

Current solution

For now I decided to use simplest solution so I copied HAL Browser to static resources and created controller that redirects to static index.html.

Now HAL Browser can be accessed at http://localhost:8080/browser/index.html

Note
We could probably extract static files and serve them with nginx, but for now this is good enough.

AsciiDoctor

AsciiDoctor creates this snippets. Snippets are ordered in a way they should appear in documentation. Not all snippets should be used for every endpoint.

Snippet Description

path-parameters

List of path parameters with descriptions

request-headers

List of request headers with descriptions

request-parameters

List of request parameters with descriptions

request-fields

List of fields in request with descriptions

request-body

Example of request body

response-fields

List of fields in response with descriptions

links

List of links in response with descriptions

curl-request

Example of curl request

http-request

Example of http request

httpie-request

Example og HTTPie request

http-response

Example of http response

response-body

Example of response body

API Documentation

API Documentation is created with Spring REST Docs library and it is packaged with application.

When running application you can access API documentation at http://localhost:8080/docs/api-guide.html

User Guide

User Guide is created with Spring REST Docs library and it is packaged with application.

When running application you can access API documentation at http://localhost:8080/docs/user-guide.html

H2 Console

When running in development mode H2 database is used and you can access with H2 Console http://localhost:8080/h2-console/

Development workflow

When new endpoint is being developed you should add objects in this order.

  1. ControllerSpecification (groovy)

  2. Controller/Method (java)

  3. ServiceSpecification - if needed (groovy)

  4. Service - if needed (java)

  5. RepositorySpecification - if needed (groovy)

  6. Repository - if needed (java)

  7. User Guide documentation (adoc)

  8. Method Documentation (java)

Running application

First you need to build application with:

./gradlew clean build

Then you can star application with:

java -jar build/libs/spring-hal-browser-learning-test-0.0.1-SNAPSHOT.jar

Debugging build

./gradlew -Dorg.gradle.debug=true -Penv=dev clean build

Profiles

There are two profiles. DEFAULT profile is default profile that is using H2 database and DEV profile that is using PostgreSQL database.

DEV profile is using liquibase to version database and for DEFAULT profile database is created by hibernate.

Default profile

Building package:

./gradlew build

Running application:

java -jar build/libs/spring-hal-browser-learning-test-0.0.1-SNAPSHOT.jar

Dev profile

Building package:

./gradlew -Penv=dev build

Starting Postgres database docker:

docker run -it --rm --name hal-postgres -p 5432:5432 -e POSTGRES_USER=hal -e POSTGRES_PASSWORD=hal postgres

Application can be run with dev profile from ide or from terminal with:

java -jar build/libs/spring-hal-browser-learning-test-0.0.1-SNAPSHOT.jar

Testing

In this project I have created both java unit tests with Junit5 and groovy unit tests with Spock. Integration tests are also done with Groovy and Junit5, and they are separated in separate directory. Jacoco makes reports for both separate and one report for both.

Mutation testing with PITest

They can work with both JUnit4 (default) and with JUnit5 (configuration via plugin), but not with both at the same time.

Also Integration tests can be included but then runtime is considerable longer and much more timeout errors are recorded.

PITest are not included in basic build process because it slows down build, but it can be run separately with:

./gradlew pitest

Conclusions

I have made sliced Unit tests. And they are grate for testing repository classes, especially custom methods that are created by naming conventions.

Unit tests for services should also use @DataJpaTest with TestEntityManager, otherwise there is lot of mocking repository responses so in my opinion that makes tests brittle, and it is questionable can TDD be done that way.

Unit test for controllers can also be done with @WebMvcTest and @MockBean and test configuration for spring security, but after that much mocking doesen’t that test become too brittle?

For now I will focus on Integration tests, they are order of magnitude or two slower then unit tests but they give good overview of how good is project, and for small scale it is not too slow. I will revisit this conclusion after reading Michael Feathers - Working Effectively with Legacy Code.

I shuld also try to add new functionality just by TDD and Unit tests.