// settings
:doctype: book :hide-uri-scheme: :icons: :toc: macro :toclevels: 3 :numbered: 1 :sectlinks: true :sectanchors: true
// badges :badge-style: plastic :user-github: astubbs :repo-github: truth-generator
:shieldio-base: https://img.shields.io :gh-shield-base: {shieldio-base}/github
:Google Truth: https://truth.dev/[Google Truth] :maven-shield: {shieldio-base}/maven-central/v/io.stubbs/truth-generator.png?style={badge-style} :maven-link: https://search.maven.org/artifact/io.stubbs/truth-generator :stackoverflow-shield: :stackoverflow-link: https://stackoverflow.com/questions/tagged/google-truth
// watcher //image:{gh-shield-base}/watchers/{user-github}/{repo-github}?&style={badge-style}[] // stars //image:{gh-shield-base}/stars/{user-github}/{repo-github}?style={badge-style}[] // forks //image:{gh-shield-base}/forks/{user-github}/{repo-github}?label=Fork&style={badge-style}[] // Github Releases - none yet //image:{gh-shield-base}/v/release/{user-github}/{repo-github}?display_name=tag&style={badge-style}[] // //image:{gh-shield-base}/v/release/{user-github}/{repo-github}?display_name=tag&include_prereleases&style={badge-style}[] // //image:{gh-shield-base}/v/release/{user-github}/{repo-github}?display_name=tag&sort=semver&style={badge-style}[] // //image:{gh-shield-base}/v/release/{user-github}/{repo-github}?display_name=tag&include_prereleases&sort=semver&style={badge-style}[] // //image:{gh-shield-base}/v/release/{user-github}/{repo-github}?display_name=release&include_prereleases&sort=date&style={badge-style}[] // Libraries.io - nothing released yet //image:{shieldio-base}//librariesio/dependent-repos/:platform/:packageName?style={badge-style}[] //image:{shieldio-base}//librariesio/dependents/:platform/:packageName?style={badge-style}[] // repo1 release //image:{maven-shield}[link={maven-link},title=Maven Release]
image:{gh-shield-base}/workflow/status/astubbs/truth-generator/CI?style={badge-style}[GitHub Workflow Status] image:{gh-shield-base}/commit-activity/m/{user-github}/{repo-github}?style={badge-style}[] image:{gh-shield-base}/last-commit/{user-github}/{repo-github}?style={badge-style}[] image:{gh-shield-base}/license/astubbs/truth-generator?style={badge-style}[GitHub] image:{shieldio-base}/badge/stackoverflow-google‐truth-5555ff.png?style={badge-style}[link={stackoverflow-link}]
= Truth Generator Subject generator library for {GoogleTruth}.
Inspired by the https://github.com/assertj/assertj-assertions-generator-maven-plugin[code generator] for https://github.com/assertj/assertj-core[AssertJ].
:github_name: parallel-consumer :base_url: https://github.com/confluentinc/{github_name} :issues_link: {base_url}/issues
// dynamic include base for editing in IDEA :project_root: ./
ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: :important-caption: :heavy_exclamation_mark: :caution-caption: :fire: :warning-caption: :warning: endif::[]
toc::[]
== Features
=== Primary
assertThat(person).hasAddress().hasStreet().ignoringCase().endsWith('Rd')
Subject
s so you don’t have to register them yourself - including going into external and/or restricted modules (i.e. java's UUID
, ZonedDateTime
etc)
** For example, you register only a single class with the plugin, and it will generate Subject's for every single referenced class in the tree.Subject
sTruth
library injected into generated code, e.g.:
MyStringSubject
: Equality check ignoring white space and line endings, file content equality
MyMapSubject
: key content check and othersjava.*
classes - UUID
etc) Tree depth limits can potentially be added to limit the quantity of generated code.=== Secondary
Subject
s under version control - won't regenerate, and will be injected into the generated structuregeneric
types are read and used in generated assertions
** i.e. assertThat(Map<MyKey, ?> map)#containsKey(MyKey key)
== Overview
The plugin will generate template code that's required by Truth to implement your own Subjects. This is mostly boilerplate and gets tiresome to write everytime you want to introduce a new Subject.
The library will also generate a lot of typical assertion methods, based on examining the classes under test.
Three Classes will be generated:
Parent
User Managed
Child
The role of these classes is:
Parent
class contains the generated assertions - more will be added as the library evolves.User Managed
class is basically empty.
Its purpose is to, if the user desires, to be a template to be added to with user created assertion methods, and brought into the users' projects' version control system.child
class merges the two classes, and contains the factory and entry points for users' test code to bind into.The one other class that is generated is the "Entry Pont" class, atm called ManagedTruth
.
This class contains a collection of all the assertThat
entry points from all the generated classes.
This is a convenience class so that the user can simply import ManagedTruth.assertThat
, and will then access to all the assertion entry points for all generated classes.
Two directories are creates under 'target/generated-sources':
truth-assertions-managed
- contains files that are not to be changed, and will evolve over time as features are added.truth-assertions-templates
- contains the templates for users to add their own assertion methods to.
When you do - you should move it from the generated
sources' dir, and put it under source control.
If any of these types of classes detected upon further plugin runs, they will not be generated again and will be injected into the assertions hierarchy dynamically.== Usage
To see what happens when this is used, see the simple <
To see how this is being used in a real world problem, take a look https://github.com/confluentinc/parallel-consumer/pull/249[at how it's used in the Confluent Parallel Consumer] library.
=== Distribution
We're not yet publishing to repo1, but we are publishing to both GitHub Packages and Package Cloud. This is because although GitHub is a giant and trustworthy, it does not https://stackoverflow.com/questions/58438367/how-to-access-maven-dependecy-from-github-package-registry-beta#comment111143283_58453517[allow anonymous access to it's package system]. Once must have set up an authentication token to use, so it's a pain.
https://packagecloud.io[Package Cloud] on the other hand works great, but some may not want to use it, in favour of GitHub. So for now, we publish to both.
Once repo1 publishing is set up, this won't be necessary.
==== Package Cloud
You can see what's https://packagecloud.io/astubbs/truth-generator[inside the repo], and what https://packagecloud.io/app/astubbs/truth-generator/search?q=io.stubbs.truth%3A[package we're publishing].
. Simply add this repository to your build:
==== GitHub Packages
. Setup your access token, with https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry[these instructions]. . Then add the following repository:
=== Setup
<dependencies>
<dependency>
<groupId>io.stubbs.truth</groupId>
<artifactId>truth-generator-api</artifactId>
<scope>test</scope>
</dependency>
... snip ...
</dependencies>
... snip ...
<build>
<plugins>
<plugin>
<!-- mvn io.stubbs.truth:truth-generator-maven-plugin:generate -->
<groupId>io.stubbs.truth</groupId>
<artifactId>truth-generator-maven-plugin</artifactId>
<configuration>
<classes>
<param>io.stubbs.truth.generator.testModel.MyEmployee</param>
</classes>
<legacyClasses>
<param>io.stubbs.truth.generator.testing.legacy.NonBeanLegacy</param>
</legacyClasses>
<packages>
<package>io.stubbs.truth.generator.testModel.package</package>
</packages>
<entryPointClassPackage>io.stubbs.truth.extensions.tests.projectUnderTest</entryPointClassPackage>
</configuration>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
... snip ...
</plugins>
</build>
[[example]] == Example
Given a simple class Car
below, with very few fields or referenced classes, the following classes are generated.
Car
[source,java]@lombok.Value public class Car { String name; Make make; int colourId;
public enum Make {PLASTIC, METAL}
Car
[source,java]/**
@see CarChildSubject */ @Generated("truth-generator") public class CarParentSubject extends Subject {
protected final Car actual;
protected CarParentSubject(FailureMetadata failureMetadata, io.stubbs.truth.generator.example.Car actual) { super(failureMetadata, actual); this.actual = actual; }
/**
/**
/**
/**
/**
/**
/**
/**
/**
Car
- if you wanted to add your own methods, you would move this source file into VCS, then add them as you see git. It will automatically be used in future generator runs. The same goes for the Car.Make
Subject
.
[source,java]/**
@see CarParentSubject */ @UserManagedSubject(Car.class) @Generated("truth-generator") public class CarSubject extends CarParentSubject {
protected CarSubject(FailureMetadata failureMetadata, io.stubbs.truth.generator.example.Car actual) { super(failureMetadata, actual); }
/**
Subject
for Car
[source,java]/**
@see CarParentSubject */ @Generated("truth-generator") public class CarChildSubject extends CarSubject {
/**
/**
/**
Car.Make
enum
[source,java]/**
@see MakeChildSubject */ @Generated("truth-generator") public class MakeParentSubject extends Subject {
protected final Make actual;
protected MakeParentSubject(FailureMetadata failureMetadata, Make actual) { super(failureMetadata, actual); this.actual = actual; }
/**
/**
/**
Car.Make
[source,java]/**
@see MakeParentSubject */ @UserManagedSubject(Make.class) @Generated("truth-generator") public class MakeSubject extends MakeParentSubject {
protected MakeSubject(FailureMetadata failureMetadata, Make actual) { super(failureMetadata, actual); }
/**
Car.Make
[source,java]/**
@see MakeParentSubject */ @Generated("truth-generator") public class MakeChildSubject extends MakeSubject {
/**
/**
/**
/**
Single point of access for all managed Subjects. */ public class ManagedTruth {
/**
/**
/**
/**
== Development
=== Releasing
When master branch is read to release: run the release workflow from GitHub actions. It will by default start the next dev version as an incremented patch version.
When ready to start a new major or minor version, use versions:set command.