{"id":24504348,"url":"https://github.com/vegardit/haxe-doctest","last_synced_at":"2026-02-09T15:02:59.144Z","repository":{"id":8993902,"uuid":"60461071","full_name":"vegardit/haxe-doctest","owner":"vegardit","description":"A haxelib inspired by Python's doctest command that generates unit tests based on assertions specified within the source code.","archived":false,"fork":false,"pushed_at":"2025-06-06T21:12:03.000Z","size":479,"stargazers_count":23,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-06T22:24:48.834Z","etag":null,"topics":["doctest","haxe","haxelib","unit-testing"],"latest_commit_sha":null,"homepage":"http://vegardit.github.io/haxe-doctest/","language":"Haxe","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vegardit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2016-06-05T13:22:46.000Z","updated_at":"2025-06-06T21:12:05.000Z","dependencies_parsed_at":"2025-03-15T08:37:51.831Z","dependency_job_id":null,"html_url":"https://github.com/vegardit/haxe-doctest","commit_stats":null,"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/vegardit/haxe-doctest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegardit%2Fhaxe-doctest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegardit%2Fhaxe-doctest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegardit%2Fhaxe-doctest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegardit%2Fhaxe-doctest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vegardit","download_url":"https://codeload.github.com/vegardit/haxe-doctest/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegardit%2Fhaxe-doctest/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267368932,"owners_count":24076093,"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","status":"online","status_checked_at":"2025-07-27T02:00:11.917Z","response_time":82,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["doctest","haxe","haxelib","unit-testing"],"created_at":"2025-01-21T23:23:13.053Z","updated_at":"2026-02-09T15:02:59.117Z","avatar_url":"https://github.com/vegardit.png","language":"Haxe","funding_links":[],"categories":[],"sub_categories":[],"readme":"# haxe-doctest - Haxedoc based unit testing.\n\n[![Build Status](https://github.com/vegardit/haxe-doctest/workflows/Build/badge.svg \"GitHub Actions\")](https://github.com/vegardit/haxe-doctest/actions?query=workflow%3A%22Build%22)\n[![Release](https://img.shields.io/github/release/vegardit/haxe-doctest.svg)](http://lib.haxe.org/p/haxe-doctest)\n[![License](https://img.shields.io/github/license/vegardit/haxe-doctest.svg?label=license)](#license)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.1%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)\n\n1. [What is it?](#what-is-it)\n1. [Declaring test assertions](#declaring-test-assertions)\n1. [Why doc-testing?](#why-doc-testing)\n1. [Doc-testing with Tink Testrunner](#doctest-with-tink)\n1. [Doc-testing with Haxe Unit](#doctest-with-haxeunit)\n1. [Doc-testing with MUnit](#doctest-with-munit)\n1. [Doc-testing with UTest](#doctest-with-utest)\n1. [Doc-testing with hx.doctest.DocTestRunner](#doctest-testrunner)\n1. [Doc-testing with FlashDevelop](#doctest-testrunner)\n1. [Installation](#installation)\n1. [Using the latest code](#latest)\n1. [License](#license)\n\n\n## \u003ca name=\"what-is-it\"\u003e\u003c/a\u003eWhat is it?\n\nA [haxelib](http://lib.haxe.org/documentation/using-haxelib/) inspired by Python's [doctest](https://docs.python.org/2/library/doctest.html) command that\ngenerates unit tests based on assertions declared within the Haxedoc comments of source code.\n\n`haxe-doctest` supports the generation of test cases for [Haxe Unit](http://haxe.org/manual/std-unit-testing.html),\n[MUnit](https://github.com/massiveinteractive/MassiveUnit), and it's own [test runner](#doctest-testrunner) which is recommended for efficient testing from\nwithin FlashDevelop.\n\n### Haxe compatiblity\n\n|haxe-doctest    |Haxe           |\n|----------------|---------------|\n|0.1.0 to 1.1.4  |3.2.1 or higher|\n|1.2.0 to 2.0.1  |3.4.2 or higher|\n|3.0.0 or higher |4.0.5 or higher|\n|3.3.0 or higher |4.2.0 or higher|\n\n\n## \u003ca name=\"declaring-test-assertions\"\u003e\u003c/a\u003eDeclaring test assertions\n\nDoc-test assertions are written as part of the source code documentation and are identified by three leading right angle brackets `\u003e\u003e\u003e` before the assertion.\n\nThe left and the right side of the assertion must be separated by one of the comparison operators `\u003c`, `\u003e`, `==`, `!=`, `===`, `!==`, `\u003c=`, `\u003e=` or the `throws` keyword.\n\nIf the right side expression is a regeex, e.g. `~/my string/` then it will be matched against the string representation of the left side expression.\n\nIf a value to be checked needs to be calculated via multiple statements, they can be wrapped inside `({  })` where the last statement is the value to be\nchecked. As an example, see the doctest for function `MyObject.setData()` below.\n\n```haxe\nclass MyTools {\n\n   /**\n    * \u003cpre\u003e\u003ccode\u003e\n    * \u003e\u003e\u003e MyTools.isValidName(null)   == false\n    * \u003e\u003e\u003e MyTools.isValidName(\"\")     == false\n    * \u003e\u003e\u003e MyTools.isValidName(\"John\") == true\n    * \u003c/code\u003e\u003c/pre\u003e\n    */\n   public static function isValidName(str:String):Bool {\n      return str != null \u0026\u0026 str.length \u003e 0;\n   }\n}\n\n\nclass MyObject {\n\n   var data:String;\n\n   /**\n    * \u003cpre\u003e\u003ccode\u003e\n    * \u003e\u003e\u003e new MyObject(null) throws \"[data] must not be null!\"\n    * \u003e\u003e\u003e new MyObject(null) throws ~/must not be null/\n    * \u003e\u003e\u003e new MyObject(\"ab\") throws nothing\n    * \u003c/code\u003e\u003c/pre\u003e\n    */\n   public function new(data:String) {\n      if(data == null) throw \"[data] must not be null!\";\n      this.data = data;\n   }\n\n   /**\n    * \u003cpre\u003e\u003ccode\u003e\n    * \u003e\u003e\u003e new MyObject(\"ab\").length()  \u003e 1\n    * \u003e\u003e\u003e new MyObject(\"ab\").length()  \u003c= 2\n    * \u003e\u003e\u003e new MyObject(\"abc\").length() != 2\n    * \u003c/code\u003e\u003c/pre\u003e\n    */\n   public function length():Int {\n      return data == null ? 0 : data.length;\n   }\n\n   /**\n    * \u003cpre\u003e\u003ccode\u003e\n    * \u003e\u003e\u003e ({\n    * ...    var o=new MyObject(\"cat\");\n    * ...    o.setData(\"dog\");\n    * ...    o.data;  // return data property outside expression block for comparison\n    * ... }) == \"dog\"\n    * \u003c/code\u003e\u003c/pre\u003e\n    */\n   public function setData(data:String):Void {\n      this.data = data;\n   }\n}\n```\n\n\n## \u003ca name=\"why-doc-testing\"\u003e\u003c/a\u003eWhy doc-testing?\n\n1. Doc-testing supports super fast test-driven development: First you write your method header, then the in-place documentation including your test assertions\n   defining the expected behavior and then implement until all your declared tests pass.\n\n   No need to create separate test classes with individual test methods.\n   Implementing and testing happens at the same code location.\n\n2. For users of your code, the doc-test assertions act as method documentation and code examples.\n\n3. Since doc-testing actually means testing the documentation against the documented code, a method's documentation always represents the actual behavior of\n   it's implementation and can't get accidently outdated.\n\n\n## \u003ca name=\"doctest-with-haxeunit\"\u003e\u003c/a\u003eDoc-testing with [Haxe Unit](https://haxe.org/manual/std-unit-testing.html)\n\n**IMPORTANT:** As of Haxe 4, Haxe Unit has been moved to a separate library called [hx3compat](https://lib.haxe.org/p/hx3compat/). So you need to run haxe with `-lib hx3compat`\n\nIn the `\u003cproject_root\u003e/test` folder, annotate a class extending `haxe.unit.TestCase` with `@:build(hx.doctest.DocTestGenerator.generateDocTests())`. The doc-test assertions from your source code\nwill then be added as test methods to this class.\n\n```haxe\n@:build(hx.doctest.DocTestGenerator.generateDocTests())\nclass MyHaxeUnitTest extends haxe.unit.TestCase {\n\n   public static function main() {\n      var runner = new haxe.unit.TestRunner();\n      runner.add(new MyHaxeUnitTest());\n      runner.run();\n   }\n\n   function new() {\n      super();\n   }\n}\n```\n\n\n## \u003ca name=\"doctest-with-tink\"\u003e\u003c/a\u003eDoc-testing with [Tink Testrunner](https://github.com/haxetink/tink_testrunner)\n\nIn the `\u003cproject_root\u003e/test` folder, annotate a class extending `tink.testrunner.BasicSuite` with `@:build(hx.doctest.DocTestGenerator.generateDocTests())`. The doc-test assertions from your\nsource code will then be added as test methods to this class.\n\n```haxe\n@:build(hx.doctest.DocTestGenerator.generateDocTests())\nclass MyTinkTestrunnerTest extends tink.testrunner.Suite.BasicSuite {\n\n   public static function main() {\n      Runner.run(new MyTinkTestrunnerTest());\n   }\n\n   function new() {\n      super({name: Type.getClassName(Type.getClass(this))}, []);\n   }\n}\n```\n\n\n## \u003ca name=\"doctest-with-munit\"\u003e\u003c/a\u003eDoc-testing with [MUnit](https://github.com/massiveinteractive/MassiveUnit)\n\nIn the `\u003cproject_root\u003e/test` folder, annotate a test class with `@:build(hx.doctest.DocTestGenerator.generateDocTests())`.\nThe doc-test assertions from your source code will then be added as test methods to this class.\n\n```haxe\n@:build(hx.doctest.DocTestGenerator.generateDocTests())\nclass MyMUnitDocTests {\n   public function new() { }\n}\n```\n\nThen add the test class to a testsuite\n```haxe\nclass MyMUnitDocTestSuite extends massive.munit.TestSuite {\n   public static function main() {\n      var client = new massive.munit.RichPrintClient();\n      var runner = new massive.munit.TestRunner(client);\n      runner.run([MyMUnitDocTestSuite]);\n   }\n\n   public function new() {\n      super();\n      add(MyMUnitDocTests);\n   }\n}\n```\n\n\n## \u003ca name=\"doctest-with-utest\"\u003e\u003c/a\u003eDoc-testing with [UTest](https://github.com/fponticelli/utest)\n\nIn the `\u003cproject_root\u003e/test` folder, annotate a class extending `utest.Test` with `@:build(hx.doctest.DocTestGenerator.generateDocTests())` **AND**\n`@:build(utest.utils.TestBuilder.build())` - the order is important. The doc-test assertions from your source code\nwill then be added as test methods to this class.\n\n```haxe\n@:build(hx.doctest.DocTestGenerator.generateDocTests())\n@:build(utest.utils.TestBuilder.build())\nclass MyUTestDocTests extends utest.Test {\n\n   public static function main() {\n      utest.UTest.run([new MyUTestDocTests()]);\n   }\n\n   function new() {\n      super();\n   }\n}\n```\n\n\n## \u003ca name=\"doctest-testrunner\"\u003e\u003c/a\u003eDoc-testing with hx.doctest.DocTestRunner\n\nhaxe-doctest also comes with it's own Testrunner which is recommended for local testing as it generates console output that is parseable by\n[FlashDevelop](http://www.flashdevelop.org/). When executed from within FlashDevelop, test failures will be displayed in the result panel as\nclickable errors that directly navigate your to the location in your source code.\n\nIn the `\u003cproject_root\u003e/test` folder, annotate a class extending `hx.doctest.DocTestRunner`  with `@:build(hx.doctest.DocTestGenerator.generateDocTests())`.\nThe doc-test assertions from your source code will then be added as test methods to this class.\n\n```haxe\n@:build(hx.doctest.DocTestGenerator.generateDocTests())\nclass MyDocTestRunner extends hx.doctest.DocTestRunner {\n\n   public static function main() {\n      var runner = new MyDocTestRunner();\n      runner.runAndExit();\n   }\n\n   function new() { super(); }\n}\n```\n\nTo integrate this with FlashDevelop, create a batch file in your project root folder, e.g. called `test-docs.cmd` containing:\n```bat\necho Compiling...\nhaxe -main mypackage.MyDocTestRunner ^\n   -cp src ^\n   -cp test ^\n   -neko target/neko/MyDocTestRunner.n || goto :eof\n\necho Testing...\nneko target/neko/TestRunner.n\n```\n\nIn FlashDevelop create a new macro in the macro editor (which is reachable via the menu **Macros -\u003e Edit Macros...**) containing the following statements.\n```bat\nInvokeMenuItem|FileMenu.Save\nRunProcessCaptured|$(SystemDir)\\cmd.exe;/c cd $(ProjectDir) \u0026 $(ProjectDir)\\test-docs.cmd\n```\n\nThen assign the macro a short cut, e.g. [F4].\n\nNow you can write your methods, document their behavior in the doc and by pressing [F4] your changes are saved and the doc-test assertions will be tested.\nErrors will show up as navigable events in the FlashDevelop's result panel.\n\n![](doc/flashdevelop_integration.png \"haxe-doctest Error Highlighting in FlashDevelop\")\n\n\n## \u003ca name=\"installation\"\u003e\u003c/a\u003eInstallation\n\n1. install the library via haxelib using the command:\n   ```\n   haxelib install haxe-doctest\n   ```\n\n2. use in your Haxe project\n\n   * for [OpenFL](http://www.openfl.org/)/[Lime](https://github.com/openfl/lime) projects add `\u003chaxelib name=\"haxe-doctest\" /\u003e` to your\n     [project.xml](http://www.openfl.org/documentation/projects/project-files/xml-format/)\n   * for free-style projects add `-lib haxe-doctest`  to `your *.hxml` file or as command line option when running the\n     [Haxe compiler](http://haxe.org/manual/compiler-usage.html)\n\n\n## \u003ca name=\"latest\"\u003e\u003c/a\u003eUsing the latest code\n\n### Using `haxelib git`\n\n```\nhaxelib git haxe-doctest https://github.com/vegardit/haxe-doctest main D:\\haxe-projects\\haxe-doctest\n```\n\n###  Using Git\n\n1. check-out the main branch\n   ```\n   git clone https://github.com/vegardit/haxe-doctest --branch main --single-branch D:\\haxe-projects\\haxe-doctest --depth=1\n   ```\n\n2. register the development release with haxe\n   ```\n   haxelib dev haxe-doctest D:\\haxe-projects\\haxe-doctest\n   ```\n\n\n## \u003ca name=\"license\"\u003e\u003c/a\u003eLicense\n\nAll files are released under the [Apache License 2.0](LICENSE.txt).\n\nIndividual files contain the following tag instead of the full license text:\n```\nSPDX-License-Identifier: Apache-2.0\n```\n\nThis enables machine processing of license information based on the SPDX License Identifiers that are available here: https://spdx.org/licenses/.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvegardit%2Fhaxe-doctest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvegardit%2Fhaxe-doctest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvegardit%2Fhaxe-doctest/lists"}