{"id":23810680,"url":"https://github.com/runeflobakk/record-matcher","last_synced_at":"2026-06-07T02:32:04.396Z","repository":{"id":137330646,"uuid":"490057430","full_name":"runeflobakk/record-matcher","owner":"runeflobakk","description":"A library and plugin for Maven to generate source code for Hamcrest Matchers for Java records.","archived":false,"fork":false,"pushed_at":"2024-04-18T22:59:56.000Z","size":81,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-21T18:17:51.335Z","etag":null,"topics":["hamcrest","hamcrest-matchers","java","java17","records","testing"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/runeflobakk.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":"2022-05-08T21:20:03.000Z","updated_at":"2024-02-04T15:28:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"90c480b2-5d81-43d2-ac50-481402f01580","html_url":"https://github.com/runeflobakk/record-matcher","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/runeflobakk/record-matcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runeflobakk%2Frecord-matcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runeflobakk%2Frecord-matcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runeflobakk%2Frecord-matcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runeflobakk%2Frecord-matcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/runeflobakk","download_url":"https://codeload.github.com/runeflobakk/record-matcher/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runeflobakk%2Frecord-matcher/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259444954,"owners_count":22858548,"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":["hamcrest","hamcrest-matchers","java","java17","records","testing"],"created_at":"2025-01-02T00:17:46.758Z","updated_at":"2025-06-12T10:09:15.122Z","avatar_url":"https://github.com/runeflobakk.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Maven Central Version](https://img.shields.io/maven-central/v/com.github.runeflobakk/record-matcher-maven-plugin)\n\n\n# Record Matcher Generator\n\nA library and plugin for Maven to generate source code for [Hamcrest Matchers](https://hamcrest.org/JavaHamcrest/) for [Java records](https://openjdk.org/jeps/395). The API of the generated matchers reflect the names of both the record itself as well as its components (i.e. fields), and provide facilities to incrementally constrain how specific you want to express what your expectations are.\n\nThis project is currently in its infancy, but should still be usable. You are most welcome to play around with it, and I appreciate any feedback you may have!\n\n## Example\n\n```java\nrecord Book (String title, List\u003cAuthor\u003e authors, int pageCount, Publisher publisher) { }\n```\n\nGiven you have defined the record above in your domain, this library can generate a `BookMatcher` which can be used like this:\n\n```java\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.*;\nimport static your.domain.BookMatcher.aBook();\n...\n\nassertThat(book, is(aBook().withTitle(\"Effective Java\").withAuthors(not(empty()))));\n\nList\u003cBook\u003e effectiveSeries = // resolve Effective Xyz series of books\nassertThat(effectiveSeries, everyItem(aBook().withTitle(containsString(\"Effective\"))));\n```\n\n\n\n## Getting started\n\nLikely, you want to use this via the Maven plugin, and this is how you would set that up in your `pom.xml`:\n\n\n```xml\n\u003cbuild\u003e\n\t\u003cplugins\u003e\n\t\t\u003cplugin\u003e\n\t\t\t\u003cgroupId\u003ecom.github.runeflobakk\u003c/groupId\u003e\n\t\t\t\u003cartifactId\u003erecord-matcher-maven-plugin\u003c/artifactId\u003e\n\t\t\t\u003cversion\u003e0.2.0\u003c/version\u003e \u003c!-- replace with any newer version --\u003e\n\t\t\t\u003cconfiguration\u003e\n\t\t\t\t\u003cincludes\u003e\n\t\t\t\t\t\u003c!--\n\t\t\t\t\tFor now, the records for which you want matchers generated needs to be enumerated.\n\t\t\t\t\tAn automated discovery facility is planned for later.\n\t\t\t\t\t--\u003e\n\t\t\t\t\t\u003cinclude\u003eyour.domain.SomeRecord\u003c/include\u003e\n\t\t\t\t\t\u003cinclude\u003eyour.domain.sub.SomeOtherRecord\u003c/include\u003e\n\t\t\t\t\u003c/includes\u003e\n\t\t\t\u003c/configuration\u003e\n\t\t\t\u003cexecutions\u003e\n\t\t\t\t\u003cexecution\u003e\n\t\t\t\t\t\u003cgoals\u003e\n\t\t\t\t\t\t\u003cgoal\u003egenerate\u003c/goal\u003e\n\t\t\t\t\t\u003c/goals\u003e\n\t\t\t\t\u003c/execution\u003e\n\t\t\t\u003c/executions\u003e\n\t\t\u003c/plugin\u003e\n\t\t...\n```\nOr separate the execution binding and configuration by putting the configuration into `pluginManagement`, if you prefer.\n\nThe configuration above will generate the source code for `SomeRecordMatcher` and `SomeOtherRecordMatcher` and put them in `target/generated-test-sources/record-matchers` in the corresponding packages as each record they match on.\n\nThe plugin will itself include the folder where it generates code as a test source root for the compiler. If your IDE is able to resolve source folders automatically based on any `build-helper-maven-plugin` configuration, you may also want to include this:\n\n```xml\n\u003cplugin\u003e\n\t\u003cgroupId\u003eorg.codehaus.mojo\u003c/groupId\u003e\n\t\u003cartifactId\u003ebuild-helper-maven-plugin\u003c/artifactId\u003e\n\t\u003cversion\u003e3.5.0\u003c/version\u003e\n\t\u003cexecutions\u003e\n\t\t\u003cexecution\u003e\n\t\t\t\u003cid\u003einclude-record-matchers\u003c/id\u003e\n\t\t\t\u003cgoals\u003e\n\t\t\t\t\u003cgoal\u003eadd-test-source\u003c/goal\u003e\n\t\t\t\u003c/goals\u003e\n\t\t\t\u003cconfiguration\u003e\n\t\t\t\t\u003csources\u003e\n\t\t\t\t\t\u003csource\u003etarget/generated-test-sources/record-matchers\u003c/source\u003e\n\t\t\t\t\u003c/sources\u003e\n\t\t\t\u003c/configuration\u003e\n\t\t\u003c/execution\u003e\n\t\u003c/executions\u003e\n\u003c/plugin\u003e\n```\n\nEclipse detects this, and to my knowledge IntelliJ should also support this. Alternatively, you will need to manually add `target/generated-test-sources/record-matchers` as a test source folder for your project in your IDE.\n\nAfter running a build, or `mvn generate-test-sources`, you should be able to see the generated Matcher classes in your IDE. The example given above would make a `SomeRecordMatcher` and `SomeOtherRecordMatcher` available (substitute with your own record(s)), and their static factory methods `SomeRecordMatcher.aSomeRecord()` and `SomeOtherRecordMatcher.aSomeOtherRecord()` which should provide their APIs via method chaining; you get autocompletion by typing `.` after the static factory method.\n\n\n\n## Use cases\n\n### Tests\n\nThis is the most obvious one, and what the project is really made for. Asserting with Hamcrest Matchers are done with the [assertThat(..)](https://hamcrest.org/JavaHamcrest/javadoc/2.2/org/hamcrest/MatcherAssert.html) method.\n\nSuppose you have this `record` returned somewhere in your code:\n\n```java\npublic record Person(UUID id, boolean isActive,\n\tString givenName, Optional\u003cString\u003e middleName, String surname,\n\tString primaryEmailAddress, List\u003cString\u003e allEmailAddresses) {}\n```\n\nAnd you need to test a case which concerns whether a person is active or not (whatever that may mean in your domain). With a Hamcrest Matcher generated by Record Matcher Generator, this can be expressed like this:\n\n```java\nimport your.domain.Person;\nimport static your.domain.PersonMatcher.aPerson; //generated by record-matcher-generator\n...\n\nPerson activePerson = //resolve the person expected to be active\nassertThat(activePerson, aPerson().withIsActive(true));\n```\n\nNow, you may ask what is the point of all this fancyness, instead of just writing `assertEquals(true, activePerson.isActive())`, see the test go green, and be on with your day? In the latter case, the only value which is known by the assertion infrastucture is a boolean, and the best it can do in case of a test failure is to say that \"expected true, but hey, it was false\", which requires you to look at the code to know _anything_ about what actually failed. In the prior case, the object from your domain is known by the assertion infrastructure, so it has the ability to also provide more context for a test failure message:\n- it was a `Person` which was not as expected\n- the property `isActive` was expected to be `true`, but was in fact `false`.\n- it may also include the whole state of the `Person` for context, which may in many cases provide clues about what happened, and you may not need to fork out a debugger to resolve where things have gotten mixed up. That is a lot more helpful than \"expected true, but was false\".\n\n\n\n### Stubbing with Mockito\n\nWhile you should keep your mocking code as simple as possible, there are cases where your stubs may need a bit of \"smartness\" to affect their behavior. Mockito allows to use Hamcrest Matchers to distinguish method invocations and how they respond, without being more specific than necessary.\n\nSee [MockitoHamcrest](https://www.javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/hamcrest/MockitoHamcrest.html).\n\n\n\n\n\n\n## License\n\nThe project is licensed as open source under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruneflobakk%2Frecord-matcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruneflobakk%2Frecord-matcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruneflobakk%2Frecord-matcher/lists"}