Skip to content
Approval testing library for Java. Alleviates the burden of hand-writing assertions.
Java Shell
Branch: master
Clone or download
tyrcho ⬆️ Bump jacoco-maven-plugin from 0.8.4 to 0.8.5 (#89)
⬆️ Bump jacoco-maven-plugin from 0.8.4 to 0.8.5
Latest commit f6edc80 Oct 15, 2019

README.md

Write Them First!


Approvals-Java

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?

Just have a look at our Releases Notes!

Get Approvals-Java

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.

Maven

In your pom.xml, add this dependency:

<dependency>
    <groupId>com.github.writethemfirst</groupId>
    <artifactId>approvals-java</artifactId>
    <version>0.12.0</version>
    <scope>test</scope>
</dependency>

Gradle

In your build.gradle, add this dependency:

testCompile 'com.github.writethemfirst:approvals-java:0.12.0'

Sbt (Scala users)

In your build.sbt, add this dependency:

libraryDependencies += "com.github.writethemfirst" % "approvals-java" % "0.12.0"

Nightly Builds

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>

Requirements

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!

Why using Approvals-Java?

Approval testing basics

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:

  1. You first execute the source code you'd like to test and let it produce its usual output,
  2. You review it manually, and say if it's producing the results you expect,
  3. 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 basics

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);.

  1. The first time verify is called, a received file is generated with a representation of its argument,
  2. 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)
  3. You commit the approved file, it is now part of the unit test and specifies the behaviour of your code,
  4. Now each time verify is called, the argument is compared with the approved file.

This replaces the calls to traditional assert methods.

What about other testing libraries?

Approvals-Java is compatible with most unit test frameworks and libraries such as JUnit, AssertJ, Mockito, etc. Since it's actually doing another job.

Is it Java only?

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.

Usage examples?

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!

How to use Approvals-Java?

Please note that most of our code samples are based on the Gilded Rose Kata. Do not hesitate to check it out ;)

Sample project

First, if you'd just want a sample project to see it in action, we have one for you!

Verify a simple object

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.

Verify each file in a folder

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.

Verify a method with combinations of arguments

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

Advanced documentation

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.

Frequently Asked Questions

Don't hesitate to have a quick look at our Frequently Asked Questions before submitting an issue.

Help/Contribute

This project is completely open to any contributions! (and remember: feedback is a valuable contribution!)

Do not hesitate to:

  1. Submit issues about any feedbacks you may have about the library,
  2. Send us a Pull Request with any contribution you think about,
  3. Have a look at open issues if you want to find a topic to work on,
  4. Do not hesitate to have a look at good first issues or help wanted issues if you search for something to start with!
  5. 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.

Thanks/Inspiration

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:

The team?

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!

License

Our code is released under GNU General Public License v3.0.

You can’t perform that action at this time.