Glacier · Gleam Incremental Interactive Unit Testing
Glacier brings incremental interactive unit testing to Gleam. It is meant as a drop-in replacement for Gleeunit and it relies on it, internally.
Quick start
- Run
gleam add glacier --dev. - Open
./test/YOUR_PROJECT.gleamand replaceimport gleeunitwithimport glacierandgleeunit.main()withglacier.main(). - Run
gleam test --target erlang -- --glacierorgleam test --target javascript -- --glacier. - Save a
srcortestmodule and watch associated tests to re-run.
Notice: On Linux inotify must be installed.
Introduction
Glacier differs from Gleeunit insofar, that it let’s you:
- Pass in the
glacierflag like so:gleam test -- --glacier, save a module and only related tests will rerun. - Or Pass in a specific unit test modules to rerun
gleam test -- test/my_module_test.gleam. - If
gleam testis passed without any---arguments it behaves the same as Gleeunit. - You can still pass in
--target erlangor--target javascriptlike sogleam test --target erlang -- --glacier, or likegleam test --target javascript -- test/my_module_test.gleam.
To enable this behavior, all you have to do is add Glacier as a dev dependency, aka gleam add glacier --dev, open ./test/YOUR_PROJECT.gleam and replace gleeunit.main() with glacier.main().
Note: gleam test must only be executed from the base project directory!
Testing against Erlang & JavaScript
Run gleam test --target erlang -- --glacier and gleam test --target javascript -- --glacier in two terminals side by side.
Requirements, Installation & Usage
Requirements
Target Erlang
Development and testing only happens on very recent stable Erlang/OTP versions, and thus may or may not run on previous versions.
Depends on fs:
Target JavaScript/NodeJS
Development and testing only happens on very recent NodeJS LTS versions, and thus may or may not run on previous versions.
Depends on fsPromises.watch:
Deno is not yet supported.
Installation
This package is available on hex.pm can be added to your Gleam project:
Run gleam add glacier --dev.
Usage
- Open
./test/YOUR_PROJECT.gleamand replaceimport gleeunitwithimport glacierandgleeunit.main()withglacier.main(). - Run
gleam test -- --glacier, then:- Save any test module (within
./test) file to re-run that single test - Save any src module (within
./src) to run all associated tests. Associated tests are test modules where the module is imported or where any of the module’s imports and their import’s imports (import chain) are imported into.
- Save any test module (within
- Optional: You may find and replace all
import gleeunit/shouldwithimport glacier/shouldand removegleeunitfrom your dependencies ingleam.toml.
Documentation
Documentation can be found at https://hexdocs.pm/glacier.
How does it work?
gleam testpasses throughglacier.main()and simply executesgleeunit.main()as if Gleeunit was used directly.gleam test -- test_module_a test_module_bpasses throughglacier.main()and executesgleeunit.test_modules(modules_list)wheremodules_listis["foo", "bar"]. The given modules are checked if they exist as either.gleamor.erltest module files and then Gleeunit runs these test modules.gleam test -- --glacierentersglacier.main()and starts a file watcher: Upon changes in module files in./testit just passes those through asgleam test -- changed_test_module(so re-saving test files executes the single test), and if a module file in./srcgot changed it parses that changed module file for any imported modules and puts the module and all chained imported modules in a distinct list of modules that should be tested. Then all test module files are read and imports of those are gathered one by one and cross matched against that list. The result is a list of test modules that need to be run, which then gets done by executing a shell call similar togleam test -- detected_test_module_a detected_test_module_b detected_test_module_c etc, aka jump to step2.
Developing Glacier
git clone https://github.com/inoas/glacier.git
cd glacier
Demo for target Erlang
# Traditional test runs
gleam test --target erlang
gleam test --target erlang -- test/glacier_demo/glacier_demo_module_a_test.gleam
# Incremental interactive test
gleam test --target erlang -- --glacier
# Re-save ./src/glacier_demo/glacier_demo_module_a.gleam
Demo for target JavaScript/NodeJS
# Traditional test runs
gleam test --target javascript
gleam test --target javascript -- test/glacier_demo/glacier_demo_module_a_test.gleam
# Incremental interactive test
gleam test --target javascript -- --glacier
# Re-save ./src/glacier_demo/glacier_demo_module_a.gleam
Possible improvements
- Erlang: Introduce some delay between the file watcher picking up a change and the test running, so that if you find-replace-save-all via an editor it does not try to run the tests 10 times. This requires some medium large changes as the code to detect imports and dependent imports needs to run for 1..n modules and if test modules are affected it needs to be handled separately (added afterwards distinct, unique).
- Gleam 0.26+: Handling of JavaScript
srcandtestmodules for Deno, via https://deno.land/api@v1.29.1?s=Deno.watchFs. - Speed: Save import lists per module into an in-memory hash-table. Before a file gets parsed for its imports, build the hash and check if a cached version already exists.
- Handling of Elixir
srcandtestmodules. - Handling of JavaScript
srcandtestmodules.