Testing¶
Basilisp includes a PyTest plugin which supports running tests defined using the functions and macros in basilisp.test.
PyTest can be installed alongside Basilisp by specifying the pytest extra when installing:
pip install basilisp[pytest]
Tests should be located in a tests/ directory off of the project root, as outlined in Project Structure.
Basilisp test files should end with an .lpy suffix and the file basename should either be prefixed with test_ or suffixed with _test.
Tests can be executed using the CLI or can be run directly using PyTest’s provided CLI.
Note
Basilisp supports executing both Basilisp and Python tests in the same test suite, so long as the Python tests are written using PyTest.
Tests can be written by wrapping your logic and assertions in a deftest form.
Basic test assertions are written using the is macro.
Tests within a deftest can be wrapped in an testing macro to both document the test function and to provide more informative testing output when tests fail.
For asserting repeatedly against different inputs, you can use the are templating function.
(ns my-project.test-core
(:require [basilisp.test :refer [deftest is are testing]]))
(deftest my-test
(is true)
(testing "false is really false"
(is (not false))))
(deftest test-adding
(are [res x y] (= res (+ x y))
3 1 2
4 2 2
0 -1 1)
Testing and PYTHONPATH¶
Typical Clojure projects will have parallel src/ and test/ folders in the project root.
Project management tooling typically constructs the Java classpath to include both parallel trees for development and only src/ for deployed software.
Basilisp does not currently have such tooling, though it is planned.
The easiest solution to facilitate test discovery with Pytest (Basilisp’s default test runner) is to create a tests directory:
tests
└── myproject
└── core_test.lpy
Test namespaces can then be created as if they are part of a giant tests package:
(ns tests.myproject.core-test)
Tests can be run with:
$ basilisp test
Alternatively, you can follow the more traditional Clojure project structure by creating a test directory for your test namespaces:
test
└── myproject
└── core_test.lpy
In this case, the test namespace can start at myproject:
(ns myproject.core-test)
However, the test directory must be explicitly added to the PYTHONPATH using the --include-path (or -p or the PYTHONPATH environment variable) option when running the tests:
$ basilisp test --include-path test
Note
Test directory names can be arbitrary.
By default, the test runner searches all subdirectories for tests.
In the first example above (tests, a Python convention), the top-level directory is already in the PYTHONPATH, allowing tests.myproject.core-test to be resolvable.
In the second example (test, a Clojure convention), the test directory is explicitly added to the PYTHONPATH, enabling myproject.core-test to be resolvable.
Warning
In versions of Basilisp prior to v0.5.1, you will also want to specify --include-unsafe-path=false to disable Python prepending the empty string "" (meaning the current directory) to the path.
Without this, PyTest will attempt to collect your namespace from ./test first, which will attempt to import your test namespaces as test.{namespace}, which will fail collection.
After version v0.5.1, basilisp test automatically prepends tests and test (if either exist) to the PYTHONPATH.
You can disable this behavior by passing --include-default-test-path=false or -d false.
Test Settings¶
PyTest typically searches the entire root directory recursively for test based on its own heuristics. For projects which don’t follow those patterns, it may be necessary to configure the test discovery more precisely.
To configure Basilisp to search only specific directories for Basilisp test files, set the BASILISP_TEST_PATH variable.
Like other PATH-like variables, you can specify multiple directories separated by your operating system’s default path separator.
If this variable is not set, Basilisp tests will be discovered using PyTest’s default discovery.
Within any eligible path, Basilisp will only load up files for tests matching the regular expression pattern given in BASILISP_TEST_FILE_PATTERN.
By default, this value is (test_[^.]*|.*_test)\.(lpy|cljc).
Fixtures¶
Basilisp supports test fixtures which can serve as setup and teardown functions for either individual tests or for whole test modules.
Fixtures can be applied using the use-fixtures function.
Basilisp comes with one builtin fixture, which can generate a temporary directory for the duration of the test.
(ns my-project.test-core
(:require
[basilisp.test :as test :refer [deftest is are testing]]
[basilisp.test.fixtures :as fixtures :refer [*tempdir*]))
(test/use-fixtures :each fixtures/tempdir)
(deftest some-test
;; accessing ``*tempdir*`` here will give a directory that will be
;; cleaned up after this test is run
)
Fixtures can trivially be written by writing a basic function and passing it to use-fixtures.
For fixtures which only need to perform setup, a fixture of no arguments will suffice.
For fixtures which must perform setup and teardown or just teardown, a function of no arguments should be written and it should yield after the setup step and before the teardown.
The test framework will yield control back to the fixture function when it is time to teardown.
You can see below that the fixture uses a dynamic Var to communicate what it has done back to any tests that use this fixture.
(def ^:dynamic *tempdir* nil)
(defn tempdir
[]
(with-open [d (tempfile/TemporaryDirectory)]
(binding [*tempdir* d]
(yield))))
Warning
Basilisp test fixtures are not related to PyTest fixtures and they cannot be used interchangeably.