{"id":25932769,"url":"https://github.com/cosium/hal-mock-mvc","last_synced_at":"2026-03-14T22:50:59.824Z","repository":{"id":167888111,"uuid":"643147596","full_name":"Cosium/hal-mock-mvc","owner":"Cosium","description":"MockMvc wrapper allowing to easily test Spring HATEOAS HAL(-FORMS) endpoints","archived":false,"fork":false,"pushed_at":"2025-02-11T09:03:27.000Z","size":91,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-25T20:41:18.616Z","etag":null,"topics":["hal","hal-form","hateoas","spring-hateoas"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Cosium.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-05-20T08:44:52.000Z","updated_at":"2025-02-11T09:03:29.000Z","dependencies_parsed_at":"2023-11-09T17:29:39.856Z","dependency_job_id":"a28e278d-ae08-4423-9d2c-ce7e2618115d","html_url":"https://github.com/Cosium/hal-mock-mvc","commit_stats":null,"previous_names":["cosium/hal-mock-mvc"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cosium%2Fhal-mock-mvc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cosium%2Fhal-mock-mvc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cosium%2Fhal-mock-mvc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cosium%2Fhal-mock-mvc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Cosium","download_url":"https://codeload.github.com/Cosium/hal-mock-mvc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241763757,"owners_count":20016161,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hal","hal-form","hateoas","spring-hateoas"],"created_at":"2025-03-04T00:39:05.347Z","updated_at":"2026-03-14T22:50:59.818Z","avatar_url":"https://github.com/Cosium.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://github.com/Cosium/hal-mock-mvc/actions/workflows/ci.yml/badge.svg)](https://github.com/Cosium/hal-mock-mvc/actions/workflows/ci.yml)\n[![Maven Central](https://img.shields.io/maven-central/v/com.cosium.hal_mock_mvc/hal-mock-mvc-spring-boot-starter.svg)](https://central.sonatype.com/artifact/com.cosium.hal_mock_mvc/hal-mock-mvc)\n\n# HAL Mock MVC\n\nMockMvc wrapper allowing to easily test [Spring HATEOAS](https://github.com/spring-projects/spring-hateoas) HAL(-FORMS) endpoints.\n\n# Prerequisites\n\n- Java 17+\n- Spring dependencies matching Spring Boot 4 and above.\n\n# Quick start\n\n1. Add the `spring-boot-starter` dependency:\n    ```xml\n    \u003cdependency\u003e\n       \u003cgroupId\u003ecom.cosium.hal_mock_mvc\u003c/groupId\u003e\n       \u003cartifactId\u003ehal-mock-mvc-spring-boot-starter\u003c/artifactId\u003e\n       \u003cversion\u003e${hal-mock-mvc.version}\u003c/version\u003e\n       \u003cscope\u003etest\u003c/scope\u003e\n    \u003c/dependency\u003e\n    ```\n2. Annotate your test class with `AutoConfigureHalMockMvc` and inject `HalMockMvc`:\n   ```java\n   @AutoConfigureHalMockMvc\n   @SpringBootTest\n   class MyTest {\n     @Autowired\n     private HalMockMvc halMockMvc;\n   \n     @Test\n     void test() {\n       halMockMvc\n          .follow(\"current-user\")\n          .get()\n          .andExpect(status().isOk())\n          .andExpect(jsonPath(\"$.alias\").value(\"jdoe\"));\n     }\n   }\n   ```\n\n# Usage\n\n## Following HAL links\n\nFollow a single relation from the base URI:\n\n```java\nhalMockMvc\n    .follow(\"users\")\n    .get()\n    .andExpect(status().isOk());\n```\n\nChain multiple hops to traverse deeper:\n\n```java\nhalMockMvc\n    .follow(\"users\", \"first\")\n    .get()\n    .andExpect(status().isOk())\n    .andExpect(jsonPath(\"$.name\").value(\"jdoe\"));\n```\n\nUse `Hop` with URI template parameters:\n\n```java\nhalMockMvc\n    .follow(Hop.relation(\"file\").withParameter(\"id\", \"foo\"))\n    .get()\n    .andExpect(status().isOk());\n```\n\n## HTTP methods\n\nShorthand methods are available directly on the traversal builder:\n\n```java\n// GET\nhalMockMvc.follow(\"users\").get()\n    .andExpect(status().isOk());\n\n// POST with JSON body\nhalMockMvc.follow(\"users\").post(\"{\\\"name\\\":\\\"john\\\"}\")\n    .andExpect(status().isCreated());\n\n// PUT with JSON body\nhalMockMvc.follow(\"user\").put(\"{\\\"name\\\":\\\"jane\\\"}\")\n    .andExpect(status().isNoContent());\n\n// PATCH with JSON body\nhalMockMvc.follow(\"user\").patch(\"{\\\"name\\\":\\\"jane\\\"}\")\n    .andExpect(status().isNoContent());\n\n// DELETE\nhalMockMvc.follow(\"user\").delete()\n    .andExpect(status().isNoContent());\n```\n\n## HAL-FORMS templates\n\nDiscover a template by key and submit it with raw JSON:\n\n```java\nhalMockMvc\n    .follow()\n    .templates()\n    .byKey(\"create\")\n    .submit(\"{\\\"name\\\":\\\"john\\\"}\")\n    .andExpect(status().isCreated());\n```\n\nSubmit a template with no body (e.g. DELETE affordance):\n\n```java\nhalMockMvc\n    .follow()\n    .templates()\n    .byKey(\"deleteByName\")\n    .submit()\n    .andExpect(status().isNoContent());\n```\n\nList all available templates:\n\n```java\nCollection\u003cTemplate\u003e templates = halMockMvc\n    .follow()\n    .templates()\n    .list();\n\n// Each Template exposes key() and representation()\n// TemplateRepresentation exposes method(), contentType(), and target()\n```\n\n## Form builder\n\nUse `createForm()` on a template for typed, validated form population:\n\n```java\nhalMockMvc\n    .follow()\n    .templates()\n    .byKey(\"create\")\n    .createForm()\n    .withString(\"name\", \"john\")\n    .withInteger(\"age\", 30)\n    .withBoolean(\"active\", true)\n    .submit()\n    .andExpect(status().isCreated());\n```\n\nAvailable typed methods: `withString`, `withBoolean`, `withInteger`, `withLong`, `withDouble`.\nCollection variants: `withStrings`, `withBooleans`, `withIntegers`, `withLongs`, `withDoubles`.\n\n## Create and shift\n\n`createAndShift()` submits, expects a `201 Created` response, then starts a new traversal from the `Location` header:\n\n```java\nhalMockMvc\n    .follow()\n    .templates()\n    .byKey(\"create\")\n    .createAndShift(\"{\\\"name\\\":\\\"john\\\"}\")\n    .follow()\n    .get()\n    .andExpect(status().isOk())\n    .andExpect(jsonPath(\"$.name\").value(\"john\"));\n```\n\n`submitAndExpect204NoContent()` submits, expects `204 No Content`, then resumes the traversal. This is useful for update-then-read flows:\n\n```java\nhalMockMvc\n    .follow()\n    .templates()\n    .byKey(\"create\")\n    .createAndShift(\"{\\\"name\\\":\\\"john\\\"}\")\n    .follow()\n    .templates()\n    .byKey(\"changeCity\")\n    .submitAndExpect204NoContent(\"{\\\"city\\\":\\\"Casablanca\\\"}\")\n    .follow()\n    .get()\n    .andExpect(status().isOk())\n    .andExpect(jsonPath(\"$.value\").value(\"Casablanca\"));\n```\n\nBoth methods are also available on the form builder (`Form#createAndShift()`, `Form#submitAndExpectNoContent()`).\n\n## Multipart requests\n\n### Direct multipart\n\nUse `multipartRequest()` on the traversal builder:\n\n```java\nbyte[] fileContent = \"hello\".getBytes(StandardCharsets.UTF_8);\nhalMockMvc\n    .follow()\n    .multipartRequest()\n    .file(\"file\", fileContent)\n    .put()\n    .andExpect(status().isNoContent());\n```\n\n### Template-based multipart\n\nUse `multipart()` on a template:\n\n```java\nhalMockMvc\n    .follow(Hop.relation(\"file\").withParameter(\"id\", \"foo\"))\n    .templates()\n    .byKey(\"uploadFile\")\n    .multipart()\n    .file(\"file\", new byte[]{0})\n    .submit()\n    .andExpect(status().isNoContent());\n```\n\nTemplate-based multipart also supports `createAndShift()`:\n\n```java\nhalMockMvc\n    .follow()\n    .templates()\n    .byKey(\"addFile\")\n    .multipart()\n    .file(\"file\", new byte[]{0})\n    .createAndShift()\n    .follow()\n    .get()\n    .andExpect(status().isOk());\n```\n\n## Request customization\n\n### Request post-processors\n\nAdd `RequestPostProcessor` instances to the builder (e.g. for authentication):\n\n```java\nHalMockMvc.builder(mockMvc)\n    .baseUri(\"/api\")\n    .addRequestPostProcessor(request -\u003e {\n        request.addHeader(\"Authorization\", \"Bearer my-token\");\n        return request;\n    })\n    .build();\n```\n\n### Custom headers\n\nSet default headers on the builder:\n\n```java\nHalMockMvc.builder(mockMvc)\n    .baseUri(\"/api\")\n    .header(\"X-Tenant-Id\", \"acme\")\n    .build();\n```\n\n## Builder customizer (Spring Boot starter)\n\nWhen using the Spring Boot starter, register a `HalMockMvcBuilderCustomizer` bean to globally customize every `HalMockMvc` instance:\n\n```java\n@TestConfiguration\nclass MyHalMockMvcConfig {\n\n    @Bean\n    HalMockMvcBuilderCustomizer securityCustomizer() {\n        return builder -\u003e builder.addRequestPostProcessor(\n            SecurityMockMvcRequestPostProcessors.user(\"admin\").roles(\"ADMIN\")\n        );\n    }\n}\n```\n\n# Genesis\n\nThis project was created following https://github.com/spring-projects/spring-hateoas/issues/733 discussion.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcosium%2Fhal-mock-mvc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcosium%2Fhal-mock-mvc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcosium%2Fhal-mock-mvc/lists"}