Approvals-Java is a lightweight open source assertion/verification library to facilitate unit testing. It alleviates the burden of hand-writing assertions.
- What's new?
- Get Approvals-Java
- Why using Approvals-Java?
- How to use Approvals-Java?
- Advanced documentation
- Frequently Asked Questions
- Help/Contribute
- Thanks/Inspiration
- The team?
- License
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:
- Windows 7+,
- Linux,
- Java 8,
- JUnit 5.
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:
- You first execute the source code you'd like to test and let it produce its usual output,
- You review it manually, and say if it's producing the results you expect,
- All future test executions will actually compare the produced results with what has been previsouly approved.
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);
.
- The first time
verify
is called, a received file is generated with a representation of its argument, - You review the content and approve it by renaming the file, (this step is usually facilitated by a merge tool detected and launched by Approvals-Java)
- You commit the approved file, it is now part of the unit test and specifies the behaviour of your code,
- Now each time
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:
- HashMaps & Collections,
- Long Strings,
- Files and folders,
- Anything with a proper toString method...
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:
- Submit issues about any feedbacks you may have about the library,
- Send us a Pull Request with any contribution you think about,
- Have a look at open issues if you want to find a topic to work on,
- Do not hesitate to have a look at good first issues or help wanted issues if you search for something to start with!
- Get in touch with us to discuss about what you'd like to contribute if you don't feel like starting alone ;)
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:
- it is not published on Maven Central, so you need to add the jar manually to your project,
- it is not actively maintained (Pull Requests are not actively merged),
- the code style is not up to Java standards (developer is mainly working with .Net).
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.