TestingΒΆ

The library that we’re using for testing is called py.test. If you’ve already done pip install -r requirements.txt, it should already be present. If not, a simple pip install pytest should do the job.

The documentation linked above covers a lot of the things very well, if something more specific is needed it’s the authoritative resource.

The names of the test files follow a simple convention. If you’re testing the code in a file called foo.py, then the test file should be called test_foo.py, and should be placed in the test directory inside sleek.guru. Essentially, the test directory should mirror the structure of the src directory, but with test_*.py files instead. [1]

Inside the test file, you can have functions that aren’t tests. In fact, they’re incredibly useful to split up testing functionality cleanly. But to mark a function as being a test, it needs to be named test_* as well. For example, test_word2vec, test_graph, etc. There are a few built-in assertions, which you can acces by import pytest, but generally, you test using only the built-in assert function. This is intended to keep tests as simple as possible. Whenever a test fails, pytest will use a feature called “assert introspection” to give you a lot of details about the failure. For example, this test:

def function(x):
    return x + 1

def test_function():
    assert(function(3) == 5)

when run, will produce this failure:

def test_function():
>       assert(function(3) == 5)
E       assert 4 == 5
E        +  where 4 = func(3)

test/test_test.py:5: AssertionError

If you want to suplly a custom assert failure message, just pass it in after the assert. However, bear in mind this will provide no assert introspection at all and will simply display the message.

One last thing. pytest provides what it calls “fixtures”, which are sort of like setup/teardown functions, except about a billion times better. As listed in their docs, fixtures have many benefits:

  • fixtures have explicit names and are activated by declaring their use from test functions, modules, classes or whole projects.
  • fixtures are implemented in a modular manner, as each fixture name triggers a fixture function which can itself use other fixtures.
  • fixture management scales from simple unit to complex functional testing, allowing to parametrize fixtures and tests according to configuration and component options, or to re-use fixtures across class, module or whole test session scopes.

There are built-in fixtures, which you can see a list of via that link, as well as by running pytest -q --fixtures. Some of the more useful of these is the cache fixture, which allows you to retain values accross test runs, the tmpdir fixture, which provides a temporary directory for tests that involve files, and the monkeypatch fixture, which provides methods to modify objects, dictionaries, and os.environ. You can access fixtures simply by putting their name in the test function arguments, like this:

def test_fixture_example(cache, monkeypatch, tmpdir):
    # now use those fixtures. it's that simple!

You can create a custom fixture like this:

import pytest

@pytest.fixture
def the_answer():
    return 42

def test_find_the_question(the_answer):
    # now we can finally solve the halting problem!

After placing the tests in the correct location and writing them, just run pytest from the sleek.guru directory and watch everything fail! [2]

That’s pretty much all you need to know. For more, see the pytest documentation.

Footnotes

[1]For more on the directory structure, see Folder Structure.
[2]They really should, because TDD!