Skip to content

Cosium/hal-mock-mvc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Maven Central

HAL Mock MVC

MockMvc wrapper allowing to easily test Spring HATEOAS HAL(-FORMS) endpoints.

Quick start

  1. Add the spring-boot-starter dependency:
    <dependency>
       <groupId>com.cosium.hal_mock_mvc</groupId>
       <artifactId>hal-mock-mvc-spring-boot-starter</artifactId>
       <version>${hal-mock-mvc.version}</version>
       <scope>test</scope>
    </dependency>
  2. Annotate your test class with AutoConfigureHalMockMvc and inject HalMockMvc:
    @AutoConfigureHalMockMvc
    @SpringBootTest
    class MyTest {
      @Autowired
      private HalMockMvc halMockMvc;
    
      @Test
      void test() {
        halMockMvc
           .follow("current-user")
           .get()
           .andExpect(status().isOk())
           .andExpect(jsonPath("$.alias").value("jdoe"));
      }
    }

Usage

Following HAL links

Follow a single relation from the base URI:

halMockMvc
    .follow("users")
    .get()
    .andExpect(status().isOk());

Chain multiple hops to traverse deeper:

halMockMvc
    .follow("users", "first")
    .get()
    .andExpect(status().isOk())
    .andExpect(jsonPath("$.name").value("jdoe"));

Use Hop with URI template parameters:

halMockMvc
    .follow(Hop.relation("file").withParameter("id", "foo"))
    .get()
    .andExpect(status().isOk());

HTTP methods

Shorthand methods are available directly on the traversal builder:

// GET
halMockMvc.follow("users").get()
    .andExpect(status().isOk());

// POST with JSON body
halMockMvc.follow("users").post("{\"name\":\"john\"}")
    .andExpect(status().isCreated());

// PUT with JSON body
halMockMvc.follow("user").put("{\"name\":\"jane\"}")
    .andExpect(status().isNoContent());

// PATCH with JSON body
halMockMvc.follow("user").patch("{\"name\":\"jane\"}")
    .andExpect(status().isNoContent());

// DELETE
halMockMvc.follow("user").delete()
    .andExpect(status().isNoContent());

HAL-FORMS templates

Discover a template by key and submit it with raw JSON:

halMockMvc
    .follow()
    .templates()
    .byKey("create")
    .submit("{\"name\":\"john\"}")
    .andExpect(status().isCreated());

Submit a template with no body (e.g. DELETE affordance):

halMockMvc
    .follow()
    .templates()
    .byKey("deleteByName")
    .submit()
    .andExpect(status().isNoContent());

List all available templates:

Collection<Template> templates = halMockMvc
    .follow()
    .templates()
    .list();

// Each Template exposes key() and representation()
// TemplateRepresentation exposes method(), contentType(), and target()

Form builder

Use createForm() on a template for typed, validated form population:

halMockMvc
    .follow()
    .templates()
    .byKey("create")
    .createForm()
    .withString("name", "john")
    .withInteger("age", 30)
    .withBoolean("active", true)
    .submit()
    .andExpect(status().isCreated());

Available typed methods: withString, withBoolean, withInteger, withLong, withDouble. Collection variants: withStrings, withBooleans, withIntegers, withLongs, withDoubles.

Create and shift

createAndShift() submits, expects a 201 Created response, then starts a new traversal from the Location header:

halMockMvc
    .follow()
    .templates()
    .byKey("create")
    .createAndShift("{\"name\":\"john\"}")
    .follow()
    .get()
    .andExpect(status().isOk())
    .andExpect(jsonPath("$.name").value("john"));

submitAndExpect204NoContent() submits, expects 204 No Content, then resumes the traversal. This is useful for update-then-read flows:

halMockMvc
    .follow()
    .templates()
    .byKey("create")
    .createAndShift("{\"name\":\"john\"}")
    .follow()
    .templates()
    .byKey("changeCity")
    .submitAndExpect204NoContent("{\"city\":\"Casablanca\"}")
    .follow()
    .get()
    .andExpect(status().isOk())
    .andExpect(jsonPath("$.value").value("Casablanca"));

Both methods are also available on the form builder (Form#createAndShift(), Form#submitAndExpectNoContent()).

Multipart requests

Direct multipart

Use multipartRequest() on the traversal builder:

byte[] fileContent = "hello".getBytes(StandardCharsets.UTF_8);
halMockMvc
    .follow()
    .multipartRequest()
    .file("file", fileContent)
    .put()
    .andExpect(status().isNoContent());

Template-based multipart

Use multipart() on a template:

halMockMvc
    .follow(Hop.relation("file").withParameter("id", "foo"))
    .templates()
    .byKey("uploadFile")
    .multipart()
    .file("file", new byte[]{0})
    .submit()
    .andExpect(status().isNoContent());

Template-based multipart also supports createAndShift():

halMockMvc
    .follow()
    .templates()
    .byKey("addFile")
    .multipart()
    .file("file", new byte[]{0})
    .createAndShift()
    .follow()
    .get()
    .andExpect(status().isOk());

Request customization

Request post-processors

Add RequestPostProcessor instances to the builder (e.g. for authentication):

HalMockMvc.builder(mockMvc)
    .baseUri("/api")
    .addRequestPostProcessor(request -> {
        request.addHeader("Authorization", "Bearer my-token");
        return request;
    })
    .build();

Custom headers

Set default headers on the builder:

HalMockMvc.builder(mockMvc)
    .baseUri("/api")
    .header("X-Tenant-Id", "acme")
    .build();

Builder customizer (Spring Boot starter)

When using the Spring Boot starter, register a HalMockMvcBuilderCustomizer bean to globally customize every HalMockMvc instance:

@TestConfiguration
class MyHalMockMvcConfig {

    @Bean
    HalMockMvcBuilderCustomizer securityCustomizer() {
        return builder -> builder.addRequestPostProcessor(
            SecurityMockMvcRequestPostProcessors.user("admin").roles("ADMIN")
        );
    }
}

Prerequisites

  • Java 17+
  • Spring dependencies matching Spring Boot 4 and above.

Genesis

This project was created following spring-projects/spring-hateoas#733 discussion.

About

MockMvc wrapper allowing to easily test Spring HATEOAS HAL(-FORMS) endpoints

Topics

Resources

License

Stars

Watchers

Forks

Contributors