Approvals-Java is a lightweight open source assertion/verification library to facilitate unit testing. It alleviates the burden of hand-writing assertions.
Just have a look at our Releases Notes!
Approvals-Java is released on which means you don't need any particular Maven/Gradle configuration to retrieve it.
Also, it is written in pure Java and has no additional dependencies.
In your pom.xml
, add this dependency:
<dependency>
<groupId>com.github.writethemfirst</groupId>
<artifactId>approvals-java</artifactId>
<version>0.13.0</version>
<scope>test</scope>
</dependency>
In your build.gradle
, add this dependency:
testCompile 'com.github.writethemfirst:approvals-java:0.13.0'
In your build.sbt
, add this dependency:
libraryDependencies += "com.github.writethemfirst" % "approvals-java" % "0.13.0"
Our SNAPSHOT
versions are released on oss.jfrog.org. To use them, simply add this repository to your pom.xml
or to your settings.xml
:
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>oss-jfrog-snapshot</id>
<name>oss-jfrog-snapshot</name>
<url>https://oss.jfrog.org/artifactory/oss-snapshot-local</url>
</repository>
</repositories>
And then you can simply rely on the latest SNAPSHOT
:
<dependency>
<groupId>com.github.writethemfirst</groupId>
<artifactId>approvals-java</artifactId>
<version>0.12.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
Approvals-Java has been tested to work efficiently with:
If you use it in other contexts, do not hesitate to let us know!
Traditional unit testing is based on hand-writing assertions on the output of your method. This might sound boring for some people, or even sometimes really hard in case of working on some legacy source code.
Approval Testing is a way of approching assertions with the following principle:
Which means you no longer write assertions... You just approve the data which will be used by assertions computer by the framework.
Approvals-Java is a simple Java framework allowing you to compute verifications of what your source code is doing, relying on Approval Testing principles.
Instead of writing tons of assertions, you simply call approvals.verify(result);
.
verify
is called, a received file is generated with a representation of its argument,verify
is called, the argument is compared with the approved file.This replaces the calls to traditional assert
methods.
Approvals-Java is compatible with most unit test frameworks and libraries such as JUnit, AssertJ, Mockito, etc. Since it's actually doing another job.
Approvals-Java should be able to work fine while being called from Scala or Kotlin, at least we're working on that topic. There might be a few things to take in consideration while calling the framework though. Refer to our wiki to get some details.
Approvals-Java can be used to verify objects which would usually require several hand-written assertions, such as:
And for sure lots of other usages you will find out!
Please note that most of our code samples are based on the Gilded Rose Kata. Do not hesitate to check it out ;)
First, if you'd just want a sample project to see it in action, we have one for you!
package com.examples;
import com.github.writethemfirst.approvals.Approvals;
public class GildedRoseApprovalTest {
private Approvals approvals = new Approvals();
@Test
void approvalSwordShouldDeteriorate() {
final Item sword = new Item("basic sword", 10, 8);
approvals.verify(GildedRose.nextDay(sword));
}
}
The toString()
of sword
is used for representing the data to be stored in the approved file.
package com.examples;
import com.github.writethemfirst.approvals.Approvals;
public class GildedRoseApprovalTests {
@Test
void approvalCopySrcFolder() {
final Approvals approvals = new Approvals();
final Path output = Files.createTempDirectory("src");
FolderCopy.copyFrom(Paths.get("."), output);
approvals.verifyAgainstMasterFolder(output);
}
}
Each file in output
is checked against the master directory.
This can save you a lot of time instead of manual assertions, and still cover for limit cases like those which mutation testing detected.
package com.examples;
import com.github.writethemfirst.approvals.Approvals;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
class GildedRoseApprovalTest {
private Approvals approvals = new Approvals();
@Test
void updateQuality_pass_shouldEvolve() {
approvals.verifyAll(
singletonList("Backstage passes"),
asList(-1, 0, 1, 5, 6, 10, 11),
asList(-1, 0, 1, 10),
this::doTest);
}
private Item doTest(final String name, final int sellIn, final int quality) {
final Item[] items = new Item[]{new Item(name, sellIn, quality)};
final GildedRose app = new GildedRose(items);
app.updateQuality();
return app.items[0];
}
}
Each of the 28 (1x7x4) combinations of name
, sellIn
, quality
is used to call doTest(name, sellIn, quality)
.
The 28 results are stored in the received text file and compared with the approved text file, which should look like:
(Backstage passes, -1, -1) => Backstage passes, -2, 0
(Backstage passes, -1, 0) => Backstage passes, -2, 0
(Backstage passes, -1, 1) => Backstage passes, -2, 0
(Backstage passes, -1, 10) => Backstage passes, -2, 0
(Backstage passes, 0, -1) => Backstage passes, -1, 0
(Backstage passes, 0, 0) => Backstage passes, -1, 0
(Backstage passes, 0, 1) => Backstage passes, -1, 0
(Backstage passes, 0, 10) => Backstage passes, -1, 0
(Backstage passes, 1, -1) => Backstage passes, 0, 2
(Backstage passes, 1, 0) => Backstage passes, 0, 3
(Backstage passes, 1, 1) => Backstage passes, 0, 4
(Backstage passes, 1, 10) => Backstage passes, 0, 13
(Backstage passes, 5, -1) => Backstage passes, 4, 2
(Backstage passes, 5, 0) => Backstage passes, 4, 3
(Backstage passes, 5, 1) => Backstage passes, 4, 4
(Backstage passes, 5, 10) => Backstage passes, 4, 13
(Backstage passes, 6, -1) => Backstage passes, 5, 1
(Backstage passes, 6, 0) => Backstage passes, 5, 2
(Backstage passes, 6, 1) => Backstage passes, 5, 3
(Backstage passes, 6, 10) => Backstage passes, 5, 12
(Backstage passes, 10, -1) => Backstage passes, 9, 1
(Backstage passes, 10, 0) => Backstage passes, 9, 2
(Backstage passes, 10, 1) => Backstage passes, 9, 3
(Backstage passes, 10, 10) => Backstage passes, 9, 12
(Backstage passes, 11, -1) => Backstage passes, 10, 0
(Backstage passes, 11, 0) => Backstage passes, 10, 1
(Backstage passes, 11, 1) => Backstage passes, 10, 2
(Backstage passes, 11, 10) => Backstage passes, 10, 11
If you can't find the information you're searching for in our documentation or in our code sample, then don't hesitate to have a look at our FAQ or Javadoc.
Don't hesitate to have a quick look at our Frequently Asked Questions before submitting an issue.
This project is completely open to any contributions! (and remember: feedback is a valuable contribution!)
Do not hesitate to:
Before contributing though, please have a look at our Code of Conduct (because we value humans and their differences) and to our Contribution Guide (because we think that a few rules allow to work faster and safer).
Do not hesitate to discuss anything from those documents if you feel they need any modification though.
Approvals-Java is inspired by ApprovalTests.
We really liked the idea of approval testing but not so much the Java implementation (Github).
Our main concerns were that:
So we decided to implement quickly a subset of the initial features and deploy the dependency on Maven Central!
Thanks a lot to all the people behind Approvals, because we got the inspiration from their work!
Thanks also to all people who created those tools we love:
Write Them First! is just a bunch of french developers who strongly believe that automated tests are extremely important in software development.
Since they also value TDD or BDD, they decided to create a few (at least one) tools to make those activities easier!
Our code is released under GNU General Public License v3.0.