Godkjenn tutorial
This will take you through the process of installing, setting up, and using godkjenn for approval testing in a Python project.
Note
This tutorial will use godkjenn’s pytest integration. Godkjenn does not mandate the use of pytest, though currently it’s the only testing framework for which godkjenn provides an integration. Integrating with other frameworks is straightforward and encouraged!
Installing godkjenn
First you need to install godkjenn. The simplest and most common way to do this is with pip:
pip install godkjenn
For pytest integration you’ll also want to install the necessary plugin:
pip install "godkjenn[pytest-plugin]"
A first test
Now let’s create our first test that uses godkjenn. Create a directory to contain the code
for the rest of this tutorial. We’ll refer to it as TEST_DIR
or $TEST_DIR
.
Once you have that directory, create the file TEST_DIR/pytest.ini
. This can be empty; it just exists
to tell pytest where the “top” of your tests is.
Next create the file TEST_DIR/test_tutorial.py
with these contents:
1def test_demo(godkjenn):
2 test_data = b'12345'
3 godkjenn.verify(test_data, mime_type='application/octet-stream')
This will be mostly familiar if you’ve used pytest: it’s just a single test function with a fixture.
On line 1 we define the test function. The godkjenn
parameter tells pytest that we want to use the
godkjenn
fixture. This fixture gives us an object that we use for verifying our test data.
On line 2 we simply invent some test data. Notice that it’s a bytes
object. Godkjenn ultimately requires all of its
data to be bytes
, so for this tutorial we’ve just created some simple data. In practice, this data would be the
output from some function that you want to test.
Finally on line 3 we call godkjenn.verify()
, passing in our test_data
. This call will take the data we pass in
and compare it to the currently-accepted “golden” output. If the data we pass in does not match the accepted output, the
test is flagged as a failure. Similarly - as will happen for us - we’ll get a failure if there is no existing accepted
output.
Running the test
Now we can run the tests with pytest. For now just run the test from TEST_DIR
:
cd $TEST_DIR
pytest .
You should see some output like this:
$ pytest .
========================================================================================================= test session starts =========================================================================================================
platform darwin -- Python 3.8.0, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/abingham/repos/sixty-north/godkjenn/docs/tutorial, configfile: pytest.ini
plugins: hypothesis-6.4.0, godkjenn-2.0.1
collected 1 item
test_tutorial.py F [100%]
============================================================================================================== FAILURES ===============================================================================================================
______________________________________________________________________________________________________________ test_demo ______________________________________________________________________________________________________________
There is no accepted data
If you wish to accept the received result, run:
godkjenn -C . accept "test_tutorial.py::test_demo"
======================================================================================================= short test summary info =======================================================================================================
FAILED test_tutorial.py::test_demo
========================================================================================================== 1 failed in 0.07s ==========================================================================================================
We see that - as expected - our test failed. The report tells us that “There is no accepted data”. This means that this is the first time we’ve run the test and haven’t accepted any output for the test. We’re also given instructions on how accept the output if we believe it to be correct.
This idea of of “accepting” output is central to the notion of approval testing. At some point we have to decide that our code is correct and that the output it produces is indicative of that proper functioning. For now let’s assume that our data is correct.
Status
Before accepting the data, let’s use the godkjenn status
command to see the state of our approval tests:
$ godkjenn status
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ Test ID ┃ Status ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
│ test_tutorial.py::test_demo │ initialized │
└─────────────────────────────┴─────────────┘
This tells us that we have one approval test in our system and that its state is “initialized”. This means that it has some received data (i.e. the data from the test we just ran) but no accepted data.
Accepting the data
Since we believe that the data we passed to godkjenn.verify()
represents the correct output from our program, we want to accept it. We can
use the command provided to us in the test output:
$ godkjenn accept "test_tutorial.py::test_demo"
Now if we run status
we see don’t get any output:
$ godkjenn status
This is because all of our tests are “up-to-date”, i.e. all of that have accepted data and not received data. In order to see the status of all test-ids, including those that are up-to-date, you can use the “-a/–show-all” options of the ‘status’ command:
$ godkjenn status --show-all
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Test ID ┃ Status ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ test_tutorial.py::test_demo │ up-to-date │
└─────────────────────────────┴────────────┘
And that’s it! You’ve created your first godkjenn approval test and accepted its output.
Accepting new data
Over time, of course, your code may change such that its correct output no longer matches your accepted output. When
this happens your test will fail and you’ll have to accept the new data. To see this, let’s change our test_tutorial.py
to look like this:
1def test_demo(godkjenn):
2 test_data = b'1234567890'
3 godkjenn.verify(test_data, mime_type='application/octet-stream')
You can see on line 2 that test_data
now has more digits. When we run our test we get a failure because of this change:
$ pytest test_tutorial.py
=================================================================================================================================== test session starts ===================================================================================================================================
platform darwin -- Python 3.8.0, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/abingham/repos/sixty-north/godkjenn/docs/tutorial, configfile: pytest.ini
plugins: hypothesis-6.4.0, godkjenn-2.0.1
collected 1 item
test_tutorial.py F [100%]
======================================================================================================================================== FAILURES =========================================================================================================================================
________________________________________________________________________________________________________________________________________ test_demo ________________________________________________________________________________________________________________________________________
Received data does not match accepted
If you wish to accept the received result, run:
godkjenn -C . accept "test_tutorial.py::test_demo"
================================================================================================================================= short test summary info =================================================================================================================================
FAILED test_tutorial.py::test_demo
==================================================================================================================================== 1 failed in 0.05s ====================================================================================================================================
You can see the failure was because “Received data does not match accepted”. That is, the data we’re passing to godkjenn.verify()
doesn’t
match the accepted data.
If we run godkjenn status
again, we see a new status for our test:
$ godkjenn status
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Test ID ┃ Status ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ test_tutorial.py::test_demo │ mismatch │
└─────────────────────────────┴──────────┘
The status “mismatch” means that the received and accepted data are different.
Seeing the difference
Our job now is to decide if the received data is correct and should become the accepted. To make this decision it can be very helpful to see the accepted data, the received data, and the differenced between them.
To see the accepted data we can use the godkjenn accepted
command:
$ godkjenn accepted "test_tutorial.py::test_demo" -
12345%
Similarly, we can see the received data using the godkjenn received
command:
$ godkjenn received "test_tutorial.py::test_demo" -
1234567890%
In this case it’s pretty easy to see the difference. In other cases it might be more difficult. To help with this godkjenn also
lets you view the difference between the files with the godkjenn diff
command. By default godkjenn diff
uses a very basic diff
display:
$ godkjenn diff "test_tutorial.py::test_demo"
WARNING:godkjenn.cli:No review tools configured. Fallback differs will be used.
---
+++
@@ -1 +1 @@
-12345
+1234567890
Again, in a simple case like this, this default diff output is enough to make it clear what the difference is. In more complex cases you might need more powerful tools, though, and we’ll look at how to use those soon.
Configuring an external diff tool
The built-in diff tool in godkjenn is sufficient for simple cases, but many people have other, more sophisticated diff tools that they would prefer to use with approval testing. Godkjenn allows you to specify these tools in your configuration.
For this tutorial we’re going to configure godkjenn to use Beyond Compare as its default diff tool. To do this you need to create the file “.godkjenn/config.toml”. Put these contents in that file:
[godkjenn.differs]
default_command = "bcomp {accepted.path} {received.path}"
This is telling godkjenn to run the command “bcomp” (the Beyond Compare executble) to display diffs. The first argument to “bcomp” will be the path to the current accepted data, and the second is the path to the received data. With this configuration, whenever godkjenn needs to display a diff (e.g in the “diff” and “review” commands), it will use “bcomp”.
If you don’t have Beyond Compare installed, you can replace “bcomp” with many other commands like “diff”, “vimdiff”, and “p4diff”.
Once you’ve made this change, you can run your godkjenn diff command again and see your configure diff tool being used.
Note
Godkjenn supports fairly sophisticated configuration of diff tools, allowing you to use different diff tools for different MIME types. See the configuration documentation for details.
Accepting the new data
We’ll assume that our new data is actually correct and accept it:
godkjenn accept "test_tutorial.py::test_demo"
Once we do that we see that our status is back to “up-to-date”:
$ godkjenn status --show-all
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Test ID ┃ Status ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ test_tutorial.py::test_demo │ up-to-date │
└─────────────────────────────┴────────────┘
Reviewing multiple tests
The godkjenn diff command lets you view the difference between the accepted and received data for a single test. In many cases, though, you have several - and in some cases a great many - tests for which you need to see the diff. The godkjenn review command lets you view all of the diffs for ‘mismatch’ tests in sequence.
In effect, the review
command calls diff
for each test that’s in the mismatch state, one after the other, using
the diffing tools that you’ve configured.
To see review
in action, let’s first add a new test. Here’s the new contents of “test_tutorial.py”:
def test_demo(godkjenn):
test_data = b"1234567890"
godkjenn.verify(test_data, mime_type="application/octet-stream")
def test_second_demo(godkjenn):
test_data = b"8675309"
godkjenn.verify(test_data, mime_type="application/octet-stream")
We’ll run the tests and accept the received data in order to lay a foundation for running review
:
$ pytest test_tutorial.py
==================================================== test session starts =====================================================
platform darwin -- Python 3.8.0, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/abingham/repos/sixty-north/godkjenn/docs/tutorial/sandbox, configfile: pytest.ini
plugins: hypothesis-6.4.0, godkjenn-4.0.0
collected 2 items
test_tutorial.py FF [100%]
========================================================== FAILURES ==========================================================
_________________________________________________________ test_demo __________________________________________________________
Received data does not match accepted
If you wish to accept the received result, run:
godkjenn -C . accept "test_tutorial.py::test_demo"
______________________________________________________ test_second_demo ______________________________________________________
There is no accepted data
If you wish to accept the received result, run:
godkjenn -C . accept "test_tutorial.py::test_second_demo"
================================================== short test summary info ===================================================
FAILED test_tutorial.py::test_demo
FAILED test_tutorial.py::test_second_demo
===================================================== 2 failed in 0.03s ======================================================
$ godkjenn accept-all
Now we’ll modify the tests so that each produces different output:
def test_demo(godkjenn):
test_data = b"-- 1234567890 --"
godkjenn.verify(test_data, mime_type="application/octet-stream")
def test_second_demo(godkjenn):
test_data = b"-- 8675309 --"
godkjenn.verify(test_data, mime_type="application/octet-stream")
If you run the tests again, godkjenn status
shows that both tests are in the ‘mismatch’ state:
$ pytest test_tutorial.py
... elided ...
$ godkjenn status
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Test ID ┃ Status ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ test_tutorial.py::test_second_demo │ mismatch │
│ test_tutorial.py::test_demo │ mismatch │
└────────────────────────────────────┴──────────┘
Now if you run godkjenn review
, godkjenn
will run your configured diff tool twice, once for each test, letting
you view and potentially modify the accepted data.
Critically, if the received and accepted data are identical after godkjenn
runs your diff tool (i.e. if you edit
them through your diff tool), then godkjenn
will accept the data and mark them as ‘up-to-date’. This gives you a
conventient way to rapidly iterate through a set of received data, verifying each in turn and rapidly updating the
accepted data for each affected test.
Note
The godkjenn review
command can be very useful if you’ve a collection of received data for which you need to
manually verify each one. However, if you somehow know that all of the received data should be accepted, then it’s
even faster to use the godkjenn accept-all
command.