{"id":13468925,"url":"https://github.com/xavdid/universal-test-runner","last_synced_at":"2025-04-14T03:13:45.339Z","repository":{"id":173263536,"uuid":"649569314","full_name":"xavdid/universal-test-runner","owner":"xavdid","description":"A language-agnostic, zero-configuration test invoker","archived":false,"fork":false,"pushed_at":"2024-12-14T22:01:50.000Z","size":145,"stargazers_count":25,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-14T03:13:40.362Z","etag":null,"topics":["cli","python","testing"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/universal-test-runner","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xavdid.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-05T07:01:09.000Z","updated_at":"2025-04-10T12:10:27.000Z","dependencies_parsed_at":"2024-09-14T02:31:46.777Z","dependency_job_id":null,"html_url":"https://github.com/xavdid/universal-test-runner","commit_stats":null,"previous_names":["xavdid/universal-test-runner"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xavdid%2Funiversal-test-runner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xavdid%2Funiversal-test-runner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xavdid%2Funiversal-test-runner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xavdid%2Funiversal-test-runner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xavdid","download_url":"https://codeload.github.com/xavdid/universal-test-runner/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248813802,"owners_count":21165634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","python","testing"],"created_at":"2024-07-31T15:01:22.028Z","updated_at":"2025-04-14T03:13:45.309Z","avatar_url":"https://github.com/xavdid.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# universal-test-runner\n\nThe Universal Test Runner is a zero-configuration, language-aware way to run unit tests in any project. It installs a command, `t`, which will determine how to run your test suite (and then run it).\n\n\u003cp align=\"center\"\u003e\n   \u003ca href=\"https://github.com/xavdid/test-runner-demo/blob/main/_demo/demo-min.gif\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/xavdid/test-runner-demo/main/_demo/demo-min.gif\"/\u003e\n   \u003c/a\u003e\n\u003c/p\u003e\n\nIf you're working on a JS project, it runs `[your package manager here] test`. You've run `pytest` in this folder before? `pytest` it is. Rust project? `cargo test` coming right up. Is also clever about running all your `go` module tests (regardless of how they're organized). No matter the command, all args are passed directly into the test runner.\n\nCurrently [supports 7 languages](#supported-languages) (and their respective test frameworks). Please open an issue if I'm missing your favorite!\n\n## Installation\n\nThe easiest way to install is by using [pipx](https://pypa.github.io/pipx/):\n\n```bash\npipx install universal-test-runner\n```\n\nYou can also use brew (which will build from source and take a little longer):\n\n```bash\nbrew install xavdid/projects/universal-test-runner\n```\n\n## Usage\n\n\u003e You can also clone the [demo repo](https://github.com/xavdid/test-runner-demo) to play around with the test runner - it's got toy examples to show how tests are run in many languages!\n\nOnce installed, the command `t` will be available. Run it in a project folder's root and it'll do its best to run your unit tests:\n\n```\n% t\n-\u003e pytest\n=============================== test session starts ================================\nplatform darwin -- Python 3.11.0, pytest-7.3.1, pluggy-1.0.0\nrootdir: /Users/username/projects/test-runner\ncollected 78 items\n\ntests/test_cli.py ...                                                        [  3%]\ntests/test_context.py .....................                                  [ 30%]\ntests/test_matchers.py ..................................................    [ 94%]\ntests/test_runner.py ....                                                    [100%]\n\n================================ 78 passed in 0.08s ================================\n```\n\nIt passes all arguments and environment modifications down to the chosen test runner:\n\n```\n% t -k test_builder --verbose\n-\u003e pytest -k test_builder --verbose\n=============================== test session starts ================================\nplatform darwin -- Python 3.11.0, pytest-7.3.1, pluggy-1.0.0\ncachedir: .pytest_cache\nrootdir: /Users/username/projects/test-runner\ncollected 78 items / 77 deselected / 1 selected\n\ntests/test_context.py::test_builder PASSED                                   [100%]\n\n========================= 1 passed, 77 deselected in 0.03s =========================\n```\n\nIt prints the command it's running as part of the output. To disable that behavior, set `UTR_DISABLE_ECHO` environment variable to anything besides `0`.\n\nIf it can't guess the testing method, it will tell you so. Feel free to open an issue to request wider language support!\n\n### Debugging\n\nThe package also ships a command to surface info about itself: `universal-test-runner`. It has a few key pieces of functionality:\n\n- the `universal-test-runner --version` flag, which prints info about your installed package version\n- the `universal-test-runner debug` command, which prints info about which command would run (and why):\n\n```\n% universal-test-runner debug\n[universal-test-runner]: checking each handler for first match\n[universal-test-runner]:   Checking command 01/11: pytest\n[universal-test-runner]:     looking for: \".pytest_cache\"\n[universal-test-runner]:     no match, continuing\n[universal-test-runner]:   Checking command 02/11: py\n[universal-test-runner]:     looking for: \"tests.py\"\n[universal-test-runner]:     no match, continuing\n[universal-test-runner]:   Checking command 03/11: go_multi\n[universal-test-runner]:     looking for: \"go.mod\" and no arguments\n[universal-test-runner]:     no match, continuing\n[universal-test-runner]:   Checking command 04/11: go_single\n[universal-test-runner]:     looking for: \"go.mod\" or a file named \"..._test.go\"\n[universal-test-runner]:     no match, continuing\n\n...\n\n[universal-test-runner]: no matching test handler. To add a new one, please file an issue: https://github.com/xavdid/universal-test-runner/issues\n```\n\n### Clearing the Terminal\n\nTo clear the terminal and scrollback buffer before running the test command, set the `UTR_CLEAR_PRE_RUN` environment variable to anything besides `0`.\n\nThis functionality has been tested on iTerm2, `Terminal.app`, and Kitty. Please open an issue if it doesn't work on your terminal.\n\n## Supported Languages\n\nThis list describes how each language behaves (but not the order in which languages are matched; use the [debugger](#debugging) for that).\n\n- Python\n  - checks for `manage.py` (Django)\n  - else tries to determine if you use `pytest` in rough order of simplicity. It checks:\n    - if you've got a `.pytest-cache` or `pytest.ini`\n    - if there's a `[pytest]` line in `tox.ini`\n    - if there's a `setup.cfg` and a `[tool:pytest]` line\n    - otherwise, it tries to read `pyproject.toml`\n      - if you're on Python 3.11+, it parses the file and checks for dependency [locations](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#dependencies-optional-dependencies) [for](https://docs.astral.sh/uv/concepts/dependencies/#development-dependencies) [popular](https://python-poetry.org/docs/managing-dependencies/#dependency-groups) [tools](https://pdm-project.org/latest/usage/dependency/#add-development-only-dependencies)\n      - otherwise, it does a best-effort regex against the file contents, looking for `[tool.pytest.ini_options]` or [dependency specifiers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#dependency-specifiers) like `pytest \u003e= 2`\n  - if you're using a popular package manager (`uv`, `pdm`, `poetry`) it'll run `\u003cpackage manager\u003e run pytest`\n  - otherwise, it runs `pytest` directly under the assumption it's available on the `$PATH`\n  - lastly, if there are _any_ python-related files, it runs `python -m unittest`, which does its own discovery\n- Rust\n  - `cargo test`\n- Go\n  - if there's a `X_test.go`, then runs a plain `go test`\n  - if you pass any args at all, runs `go test your-args-here`\n  - otherwise, runs `go test ./...`\n- Elixir\n  - `mix test`\n- Clojure\n  - `lein test`\n- Javascript/Typescript\n  - if there's a `package.json` and it has a `test` script, runs `[package manager] test`, where `[package manager]` is:\n    - `npm` if there's a `package-lock.json`\n    - `yarn` if there's a `yarn.lock`\n    - `pnpm` if there's a `pnpm-lock.yaml`\n  - `bun test` if there's a `bun.lockb`\n- [Just](https://github.com/casey/just)\n  - if there are any common justfile names, it uses the JSON api to find a `test` command\n  - if `just` isn't installed, it does its best to parse the file as a string\n- Makefile\n  - looks for a line that starts with `test:`\n\n### Exercism\n\n[Exercism](https://exercism.org/) is a platform for learning new programming languages. It has more than 65 tracks available. The Universal Test Runner supports nearly all of them out of the box using the [Exercism CLI](https://exercism.org/docs/using/solving-exercises/working-locally)'s `exercism test` command. Just like this tool, it knows how to run each track's tests and invokes the correct one automatically.\n\nRather than re-implement all of the test commands `exercism` can handle, the runner will invoke the Exercism CLI when run from an exercise directory. This requires version `3.2.0` of the Exercism CLI installed.\n\n\u003e fun fact: I [added the test command](https://github.com/exercism/cli/pull/1092) after it was suggested in the [forum thread](https://forum.exercism.org/t/introducing-the-universal-test-runner/6228) where I announced the Universal Test Runner\n\n## Motivation\n\nI work in a few languages at a time, so I've actually had a [version of this in my dotfiles](https://github.com/xavdid/dotfiles/blob/6bd5f56b1f9ad2dcef9f8b72413d30779b378aef/node/aliases.zsh#L45-L73) for a while. Also, as I've been doing [Exercism's #12in23 program](https://exercism.org/challenges/12in23), I'm _really_ switching languages. It's nice not to have to re-learn any muscle memory. Plus, increasingly complex `bash` was holding me back.\n\n### Design Philosophy\n\n1. The runner itself should need no configuration - it Just Works\n2. It should pass all arguments through to the underlying test command\n3. It should have wide language and test runner support; please open an issue if your use case isn't supported!\n\n## FAQ\n\n### `just` errors when passing CLI args\n\nIf you run with args (like `t -k whatever`) and see an error from `just` like:\n\n```\nerror: Justfile does not contain recipes `-k` or `whatever`.\n```\n\nThat means your `test` recipe doesn't accept any options. Make sure it has an `*options` arg that you pass through to your test command:\n\n```justfile\ntest *options:\n    pytest {{options}}\n```\n\n## Development\n\nThis section is people making changes to this package.\n\nWhen in a virtual environment, run the following:\n\n```bash\npip install -e '.[test]'\n```\n\nThis installs the package in `--edit` mode and makes its dependencies available. You can now run `t` to run tests and `universal-test-runner` to access help, version, and debugging info.\n\n### Running Tests\n\nIn your virtual environment, a simple `pytest` should run the unit test suite. You can also run `pyright` for type checking.\n\n### Releasing New Versions\n\n\u003e these notes are mostly for myself (or other contributors)\n\n1. bump to desired version in `pyproject.toml` and add `CHANGELOG` entry\n2. Run `just release` while your venv is active\n3. paste the stored API key (If you're getting invalid password, verify that `~/.pypirc` is empty)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxavdid%2Funiversal-test-runner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxavdid%2Funiversal-test-runner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxavdid%2Funiversal-test-runner/lists"}