{"id":37069665,"url":"https://github.com/xi/assamtest","last_synced_at":"2026-01-14T08:01:21.986Z","repository":{"id":57411895,"uuid":"169867512","full_name":"xi/assamtest","owner":"xi","description":"mocha-style tests for python","archived":true,"fork":false,"pushed_at":"2019-02-11T07:13:51.000Z","size":18,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-25T13:51:24.895Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-09T13:02:12.000Z","updated_at":"2023-11-05T10:15:25.000Z","dependencies_parsed_at":"2022-09-07T23:30:53.391Z","dependency_job_id":null,"html_url":"https://github.com/xi/assamtest","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/xi/assamtest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xi%2Fassamtest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xi%2Fassamtest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xi%2Fassamtest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xi%2Fassamtest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xi","download_url":"https://codeload.github.com/xi/assamtest/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xi%2Fassamtest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28413527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2026-01-14T08:01:21.267Z","updated_at":"2026-01-14T08:01:21.910Z","avatar_url":"https://github.com/xi.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"assamtest is an experimental python test framework inspired by JavaScript\nlibraries such as [mocha](https://mochajs.org/) or\n[jasmine](https://jasmine.github.io/).\n\n-\teverything is explicit, no magic\n-\tsimple generated tests and suites\n-\tarbitrary nesting of test suites\n-\tcompatible with converage\n-\teasily extendable with decorators and custom outcomes\n\n## Usage\n\n```python\n# tests.py\nimport assamtest\nfrom assamtest import expect\n\n@assamtest.suite('A suite is just a function')\ndef my_suite():\n\t@assamtest.test('and so is a test')\n\tdef my_test():\n\t\ta = True\n\t\texpect.true(a)\n```\n\n```\n$ pip install assamtest\n$ assamtest\nA suite is just a function\n  ✓ and so is a test\n\n✓ 1 passed\n```\n\n## Why another test framework?\n\nThe idea for this library came out of my growing frustration with pytest,\nespecially its [`parametrize`][1] feature.\n\nIn jasmine, parametrization is trivial:\n\n```js\ndescribe('#isNumber', function() {\n\t[1, 1000000, 0, -1].forEach(function(i) {\n\t\tit('recognizes ' + i, function() {\n\t\t\texpect(isNumber(i)).toBe(true);\n\t\t});\n\t});\n});\n```\n\nThis is because the tests are [registered explicitly][2]. The popular python\ntest frameworks (pytest, unittest) on the other hand use an implicit mechanism\nwhere each function that starts with 'test\\_' is registered. This makes\nparametrization way harder than it needs to be.\n\nThis library is an attempt to bring the explicit approach to python. However,\nthere are two important differences between the languages that make this\napproach a bit less elegant in python:\n\n-\tThe test functions will never be called explicitly, so there is really no\n\tneed for a name. But python does not have anonymous functions. Not a big\n\tdeal, but still awkward, especially for things like `before_each` and\n\t`after_each`.\n\n-\tIn python, variables are local by default. If you want to write to variables\n\tfrom a descendant scope you have to use the `nonlocal` (or `global`) keyword.\n\n[1]: https://docs.pytest.org/en/latest/parametrize.html\n[2]: https://mochajs.org/#dynamically-generating-tests\n\n## Reference\n\n### `@test(name=None, args=[], decorators=[])`\n\nRegister a function as a test:\n\n*\t`name` (str): The name of this test (defaults to the function name)\n*\t`args` (list): Arguments that should be passed to the test function\n*\t`decorators` (list): The test function will be passed through these decorators before being executed\n\nAsync functions are automatically executed in an event loop.\n\n```python\nimport assamtest\nfrom assamtest import expect\n\n@assamtest.test(args=['+', 5])\n@assamtest.test(args=['*', 6])\ndef my_test(op, value):\n\tassamtest.expect.equal(eval('2 %s 3' % op), value)\n```\n\n### `@suite(name=None, args=[], decorators=[])`\n\nRegister a function as a suite:\n\n```python\nimport assamtest\nfrom assamtest import expect\n\n@assamtest.suite()\ndef my_suite():\n\t@assamtest.before_each()\n\tdef _before_each():\n\t\tpass  # do some setup here\n\n\t@assamtest.test()\n\tdef my_test():\n\t\texpect.equal(2 + 2, 4)\n```\n\nThe optional parameters are the same as for `test()`.\n\n### `@before()` / `@after()`\n\nRegister a function to run before/after the whole suite.\n\nThere can be only one `before`/`after` function per suite.\n\n### `@before_each()` / `@after_each()`\n\nRegister a function to run before/after every test.\n\nThere can be only one `before_each`/`after_each` function per suite.\n\n### `expect`\n\nA wrapper around the asserts from `unittest.TestCase` using snake case:\n\n```python\nfrom assamtest import expect\n\nexpect.equal(2 + 2, 4)\nexpect.not_equal(2 + 2, 5)\nexpect._in(2, [1, 2, 3])\nwith expect.raises(KeyError):\n\t{'foo': 0}['bar']\n```\n\nSee also the [full list of available assertions](https://docs.python.org/3/library/unittest.html?highlight=unittest%20testcase#assert-methods\u003e).\n\n### `@decorators.skip`\n\nDo not execute the test at all::\n\n```python\nimport assamtest\nfrom assamtest import expect\nfrom assamtest.decorators import skip\n\n@assamtest.test(decorators=[skip])\ndef my_test():\n\texpect.equal(2 + 2, 5)\n```\n\n### `@decorators.fail`\n\nInvert the result of the test: If it would fail, pass instead. If it would\npass, fail instead::\n\n```python\nimport assamtest\nfrom assamtest import expect\nfrom assamtest.decorators import fail\n\n@assamtest.test(args=[4])\n@assamtest.test(args=[5], decorators=[fail])\ndef my_test(value):\n\texpect.equal(2 + 2, value)\n```\n\n### `Outcome(err, status, level)`\n\nCan be used to implement custom outcomes.\n\n*\t`err` (Exception|str|None): The reason for this outcome, e.g. an exception or a helpful message\n*\t`status` (str): The status, e.g. 'passed', 'failed', or 'skipped'\n*\t`level` ('SUCCESS'|'INFO'|'WARNING'|'ERROR'): A hint for the reporter how this outcome should be interpreted\n\nA good example of how this can be used is `decorators.skip()`:\n\n```python\nimport functools\nfrom assamtest import Outcome\n\ndef skip(fn):\n\t@functools.wraps(fn)\n\tdef wrapper(*args, **kwargs):\n\t\traise Outcome(None, 'skipped', 'INFO')\n\treturn wrapper\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxi%2Fassamtest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxi%2Fassamtest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxi%2Fassamtest/lists"}