{"id":19486915,"url":"https://github.com/archan937/ex_united","last_synced_at":"2025-04-25T18:32:14.431Z","repository":{"id":46051393,"uuid":"248719329","full_name":"archan937/ex_united","owner":"archan937","description":"Easily spawn Elixir nodes (supervising, Mix configured, easy asserted / refuted) within ExUnit tests","archived":false,"fork":false,"pushed_at":"2021-11-17T16:25:26.000Z","size":147,"stargazers_count":43,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-19T05:57:11.446Z","etag":null,"topics":["clustering","distributed-systems","elixir","package","testing"],"latest_commit_sha":null,"homepage":"https://medium.com/@pm_engel/testing-elixir-nodes-using-exunited-b267f4dd32d8","language":"Elixir","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/archan937.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2020-03-20T09:43:26.000Z","updated_at":"2024-05-17T17:05:05.000Z","dependencies_parsed_at":"2022-09-02T14:02:18.212Z","dependency_job_id":null,"html_url":"https://github.com/archan937/ex_united","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archan937%2Fex_united","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archan937%2Fex_united/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archan937%2Fex_united/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archan937%2Fex_united/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/archan937","download_url":"https://codeload.github.com/archan937/ex_united/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250872295,"owners_count":21500794,"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":["clustering","distributed-systems","elixir","package","testing"],"created_at":"2024-11-10T20:42:26.483Z","updated_at":"2025-04-25T18:32:14.139Z","avatar_url":"https://github.com/archan937.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ExUnited [![Build Status](https://travis-ci.org/archan937/ex_united.svg?branch=master)](http://travis-ci.org/archan937/ex_united)\n\nEasily spawn Elixir nodes (supervising, Mix configured, easy asserted / refuted) within ExUnit tests\n\n## Introduction\n\n`ExUnited` is a Hex package designed to easily facilitate spawning **supervising**\nlocal Elixir nodes within tests. Unfortunately, I was not able to properly setup\na spawned node for supervisioning with the Erlang `:slave.start_link/1` function.\nSo I have written `ExUnited` to accomplish that, along with supporting `Mix.Config`\nconfigurations, additional loaded code, and a developer friendly way of writing\nassertions and refutations in the context of a spawned node which really improved\nthe readability of the tests and more.\n\n## Features\n\n  * Spawn nodes for testing purposes\n  * Spin up \"partially connected\" vs \"fully connected\" nodes\n  * Run in \"verbose\" mode which prints a colorized STDOUT of the nodes\n  * Specify extra \"code paths\" which will be included (`config.exs` included)\n  * Support child supervisioning within a spawned node\n  * Exclude certain dependencies for spawned nodes\n  * Easily(!) assert and refute within the context of spawned nodes\n\nEnjoy the package! I would love to receive a shoutout and/or your feedback ;)\n\n## Installation\n\nTo install ExUnited, please add `ex_united` to your list of dependencies in\n`mix.exs`:\n\n  ```elixir\n  def deps do\n    [\n      {:ex_united, \"~\u003e 0.1.5\", only: :test}\n    ]\n  end\n  ```\n\nReplace the default `ExUnit.start()` invocation in the test helper file with\n`ExUnited.start()`:\n\n  ```elixir\n  # test/test_helper.exs\n  ExUnited.start()\n  ```\n\n### Explicitly start ExUnit yourself\n\nAs of version `0.1.2`, you can also start `ExUnit` yourself explicitly and add\n`ExUnited.start(false)` instead:\n\n  ```elixir\n  # test/test_helper.exs\n  ExUnit.start()\n  ExUnited.start(false)\n  ```\n\n### ATTENTION: When also using meck-based packages\n\nThe following errors can occur when also using packages like\n[mock](https://hex.pm/packages/mock) or [MecksUnit](https://hex.pm/packages/mecks_unit)\n(which both use the Erlang library [meck](https://github.com/eproxus/meck) to\nmock functions) and spawning the nodes with the default environment `test`:\n\n* `(UndefinedFunctionError) function Some.Module.some_function/1 is undefined`\n* `(ErlangError) Erlang error: {{:undefined_module, \u003c Some.Module \u003e}`\n\nTo tackle this, you should configure any other (Mix) environment to spawn the\nnodes with. Configure it like so:\n\n  ```elixir\n  # config/test.exs\n  import Config\n\n  config :ex_united,\n    mix_env: :dev\n  ```\n\nYou might also want to consider using a bogus environment (e.g. `:void`) to skip\nthe non-relevant `:dev` dependencies, like `credo` or `dialyxir` probably. That\nwill save some compile time.\n\nAnd last but not least, please note that using a different environment within CI\nbuilds will require compiling the project in that particular environment on\nbeforehand of the tests. Otherwise spawning the nodes will take too much time\nand that will cause timeout errors during the tests.\n\n  ```yaml\n  # .gitlab-ci.yml\n  before_script:\n    ...\n    - MIX_ENV=void mix deps.get\n    - MIX_ENV=void mix run -e 'IO.puts(\"Done.\")'\n    - epmd -daemon\n  script:\n    - mix test\n  ```\n\n## Usage\n\nFor using `ExUnited`, the two essential functions are:\n\n  1. `ExUnited.spawn/2` - Spawns (`Mix.Config` configured, additional code loaded,\n    supervising) nodes\n  2. `ExUnited.teardown/0` - Kills the spawned nodes and it also cleans up their\n    generated files\n\n### The most simplest setup\n\nNodes can be specified as a list of atoms, just like in the following example.\nTheir node names will be `:\"bruce@127.0.0.1\"` and `:\"clark@127.0.0.1\"` respectively).\n\nPlease do not forget to invoke `ExUnited.teardown/0` at the `on_exit` hook.\n\n  ```elixir\n  setup do\n    {:ok, spawned} = ExUnited.spawn([:bruce, :clark])\n\n    on_exit(fn -\u003e\n      ExUnited.teardown()\n    end)\n\n    spawned\n  end\n  ```\n\n### \"Partially versus Fully connected\" and/or \"Verbose\" spawned nodes\n\nAs a second argument, you can pass a list of atoms for the options:\n\n  * `:connect` - if `true` a \"fully connected\" node will be spawned (see\n    the `erl -connect_all` flag for more information). Defaults to `false`\n  * `:verbose` - if `true` the STDOUT of the spawned node will be printed.\n    Defaults to `false`\n\nSee `ExUnited.spawn/2` for more information.\n\n  ```elixir\n  setup do\n    {:ok, spawned} = ExUnited.spawn([:roy], [:connect, :verbose])\n\n    on_exit(fn -\u003e\n      ExUnited.teardown()\n    end)\n\n    spawned\n  end\n  ```\n\nWhich results in the following when running tests:\n\n  ```shell\n  PME-Legend ~/S/ex_united:master\u003e mix test test/ex_united/supervised_test.exs:140\n  Excluding tags: [:test]\n  Including tags: [line: \"140\"]\n\n  iex(roy@127.0.0.1)\u003e Compiling 1 file (.ex)\n  iex(roy@127.0.0.1)\u003e Generated void app\n  iex(roy@127.0.0.1)\u003e Interactive Elixir (1.10.1) - press Ctrl+C to exit (type h() ENTER for help)\n  iex(roy@127.0.0.1)1\u003e\n  .\n\n  Finished in 0.9 seconds\n  2 tests, 0 failures, 1 excluded\n  ```\n\n### Exclude certain dependencies for all spawned nodes\n\nYou can exclude certain (Mix) dependencies for ALL spawned nodes by for instance\nadding `exclude: [:inch_ex]` to the options. This can significantly improve\nthe speed of your tests.\n\n  ```elixir\n  setup do\n    {:ok, spawned} = ExUnited.spawn([:bruce, :clark], [:verbose, exclude: [:inch_ex]])\n\n    on_exit(fn -\u003e\n      ExUnited.teardown()\n    end)\n\n    spawned\n  end\n  ```\n\nThe following dependencies are excluded by default:\n\n* `:credo`\n* `:dialyxir`\n* `:ex_doc`\n* `:ex_united`\n* `:excoveralls`\n\n### Configuring the spawned nodes\n\nAside from the list of atoms, you can also specify nodes as a keyword list in\ncase you want to configure them. The following options are available:\n\n* `:code_paths` - a list of directories that will be included\n* `:exclude` - a list of dependencies that will be excluded\n* `:supervise` - the child spec(s) used for supervisioning\n\n### Including additional code\n\nIt would be a best practice to create a directory called `test/nodes` in which\nyou put a directory containing code for a specific spawned node. Please note that\nthe file called `config.exs` is supported for `Mix.Config`:\n\n  ```elixir\n  setup do\n    {:ok, spawned} =\n      ExUnited.spawn(\n        eric: [\n          code_paths: [\n            \"test/nodes/cantona\"\n          ]\n        ]\n      )\n\n    on_exit(fn -\u003e\n      ExUnited.teardown()\n    end)\n\n    spawned\n  end\n  ```\n\nSee [test/ex_united/supervised_test.exs](https://github.com/archan937/ex_united/blob/v0.1.5/test/ex_united/supervised_test.exs#L7)\nwith its corresponding [test/nodes/ronaldo](https://github.com/archan937/ex_united/tree/v0.1.5/test/nodes/ronaldo)\nas an example.\n\n### Exclude certain dependencies for a specific spawned node\n\nAdd the `:exclude` list as follows:\n\n  ```elixir\n  setup do\n    {:ok, spawned} =\n      ExUnited.spawn(\n        bruce: [\n          code_paths: [\n            \"test/nodes/bruce\"\n          ],\n          exclude: [\n            :my_unused_dependency,\n            :my_current_project\n          ],\n          supervise: [MyAwesomeGenServer]\n        ],\n        clark: [\n          code_paths: [\n            \"test/nodes/clark\"\n          ],\n          supervise: [MyOtherAwesomeGenServer]\n        ]\n      )\n\n    on_exit(fn -\u003e\n      ExUnited.teardown()\n    end)\n\n    spawned\n  end\n  ```\n\n### Add supervisioning\n\nChildspecs should be the same argument as if you are adding them to your classic\n`\u003capp\u003e/application.ex` file:\n\n  ```elixir\n  setup do\n    {:ok, spawned} =\n      ExUnited.spawn(\n        bruce: [\n          code_paths: [\n            \"test/nodes/bruce\"\n          ],\n          supervise: [MyAwesomeGenServer]\n        ],\n        clark: [\n          code_paths: [\n            \"test/nodes/clark\"\n          ],\n          supervise: [MyOtherAwesomeGenServer]\n        ]\n      )\n\n    on_exit(fn -\u003e\n      ExUnited.teardown()\n    end)\n\n    spawned\n  end\n  ```\n\nPay attention that functions within childspecs should be quoted.\n\n  ```elixir\n  setup do\n    {:ok, spawned} =\n      ExUnited.spawn(\n        [\n          roy: [\n            code_paths: [\n              \"test/nodes/keane\"\n            ],\n            supervise: [\n              {\n                Roy,\n                talk:\n                  quote do\n                    fn\n                      1 -\u003e \"Hi, I am Roy Keane\"\n                      2 -\u003e \"I am keen as mustard\"\n                      3 -\u003e \"I like to be peachy keen\"\n                    end\n                  end\n              }\n            ]\n          ]\n        ],\n        [:verbose]\n      )\n\n    on_exit(fn -\u003e\n      ExUnited.teardown()\n    end)\n\n    spawned\n  end\n  ```\n\n### Easily assert and refute within the context of spawned nodes\n\nTo seemlessly execute assertions and refutations within spawned nodes, you can\nsetup your test module by either using `ExUnited.Case` instead of `ExUnit.Case`:\n\n  ```elixir\n  defmodule MyNodesTest do\n    use ExUnited.Case\n  end\n  ```\n\nOr by importing the `ExUnited.Case` module:\n\n  ```elixir\n  defmodule MyNodesTest do\n    use ExUnit.Case\n    import ExUnited.Case\n  end\n  ```\n\nWriting assertions and refutations within the context of a certain spawned is\npretty straight forward with the use of the `ExUnited.Case.as_node/2` function\nas if you are writing your class `assert` and/or `refute` statements:\n\n  ```elixir\n  defmodule MyNodesTest do\n    use ExUnited.Case\n\n    setup do\n      {:ok, spawned} = ExUnited.spawn([:bruce, :clark])\n\n      on_exit(fn -\u003e\n        ExUnited.teardown()\n      end)\n\n      spawned\n    end\n\n    test \"assertions and refutations within node contexts\", spawned do\n      bruce = get_in(spawned, [:bruce, :node])\n\n      as_node(bruce) do\n        assert :\"bruce@127.0.0.1\" = Node.self()\n        refute :\"clark@127.0.0.1\" == Node.self()\n      end\n\n      as_node(:clark) do\n        assert :\"clark@127.0.0.1\" = Node.self()\n        refute :\"bruce@127.0.0.1\" == Node.self()\n      end\n    end\n  end\n  ```\n\nSee `ExUnited.Case.as_node/2` for more information.\n\n## Contact me\n\nFor support, remarks and requests, please mail me at [pm_engel@icloud.com](mailto:pm_engel@icloud.com).\n\n## License\n\nCopyright (c) 2020 Paul Engel, released under the MIT License\n\nhttp://github.com/archan937 – http://twitter.com/archan937 – [pm_engel@icloud.com](mailto:pm_engel@icloud.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farchan937%2Fex_united","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farchan937%2Fex_united","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farchan937%2Fex_united/lists"}