All Projects → archan937 → ex_united

archan937 / ex_united

Licence: other
Easily spawn Elixir nodes (supervising, Mix configured, easy asserted / refuted) within ExUnit tests

Programming Languages

elixir
2628 projects

Projects that are alternatives of or similar to ex united

realtimemap-dotnet
A showcase for Proto.Actor - an ultra-fast distributed actors solution for Go, C#, and Java/Kotlin.
Stars: ✭ 47 (+17.5%)
Mutual labels:  distributed-systems, clustering
Protoactor Dotnet
Proto Actor - Ultra fast distributed actors for Go, C# and Java/Kotlin
Stars: ✭ 1,070 (+2575%)
Mutual labels:  distributed-systems, clustering
elixir cluster
Distributed Elixir Cluster on Render with libcluster and Mix Releases
Stars: ✭ 15 (-62.5%)
Mutual labels:  distributed-systems, clustering
quartz-scheduler-hazelcast-jobstore
An implementation of a Quartz Scheduler JobStore using Hazelcast distributed Collections
Stars: ✭ 42 (+5%)
Mutual labels:  distributed-systems, clustering
Dagsfm
Distributed and Graph-based Structure from Motion
Stars: ✭ 269 (+572.5%)
Mutual labels:  distributed-systems, clustering
protoactor-go
Proto Actor - Ultra fast distributed actors for Go, C# and Java/Kotlin
Stars: ✭ 4,138 (+10245%)
Mutual labels:  distributed-systems, clustering
profmem
🔧 R package: profmem - Simple Memory Profiling for R
Stars: ✭ 32 (-20%)
Mutual labels:  package
LIUM
Scripts for LIUM SpkDiarization tools
Stars: ✭ 28 (-30%)
Mutual labels:  clustering
kmeans
K-Means clustering
Stars: ✭ 51 (+27.5%)
Mutual labels:  clustering
sfpowerscripts
A build system for modular development in Salesforce, delivered as a sfdx plugin that can be implemented in any CI/CD system of choice
Stars: ✭ 121 (+202.5%)
Mutual labels:  package
calib
Calib clusters barcode tagged paired-end reads based on their barcode and sequence similarity.
Stars: ✭ 29 (-27.5%)
Mutual labels:  clustering
lannister
A lightweight MQTT broker w/ full spec,Clustering,WebSocket,SSL written in Java
Stars: ✭ 20 (-50%)
Mutual labels:  clustering
rabbitmq-peer-discovery-aws
AWS-based peer discovery backend for RabbitMQ 3.7.0+
Stars: ✭ 23 (-42.5%)
Mutual labels:  clustering
dbscan-python
[New Version] Theoretically Efficient and Practical Parallel DBSCAN
Stars: ✭ 18 (-55%)
Mutual labels:  clustering
echotron
An elegant and concurrent library for Telegram bots in Go.
Stars: ✭ 95 (+137.5%)
Mutual labels:  package
magento-cluster
Highly Available and Auto-scalable Magento Cluster
Stars: ✭ 21 (-47.5%)
Mutual labels:  clustering
desktop-settings
No description or website provided.
Stars: ✭ 23 (-42.5%)
Mutual labels:  package
fjage
Framework for Java and Groovy Agents
Stars: ✭ 19 (-52.5%)
Mutual labels:  distributed-systems
django-celery-fulldbresult
Django Celery DB Backend that keeps enough info to retry a task.
Stars: ✭ 37 (-7.5%)
Mutual labels:  distributed-systems
future.batchtools
🚀 R package future.batchtools: A Future API for Parallel and Distributed Processing using batchtools
Stars: ✭ 77 (+92.5%)
Mutual labels:  package

ExUnited Build Status

Easily spawn Elixir nodes (supervising, Mix configured, easy asserted / refuted) within ExUnit tests

Introduction

ExUnited is a Hex package designed to easily facilitate spawning supervising local Elixir nodes within tests. Unfortunately, I was not able to properly setup a spawned node for supervisioning with the Erlang :slave.start_link/1 function. So I have written ExUnited to accomplish that, along with supporting Mix.Config configurations, additional loaded code, and a developer friendly way of writing assertions and refutations in the context of a spawned node which really improved the readability of the tests and more.

Features

  • Spawn nodes for testing purposes
  • Spin up "partially connected" vs "fully connected" nodes
  • Run in "verbose" mode which prints a colorized STDOUT of the nodes
  • Specify extra "code paths" which will be included (config.exs included)
  • Support child supervisioning within a spawned node
  • Exclude certain dependencies for spawned nodes
  • Easily(!) assert and refute within the context of spawned nodes

Enjoy the package! I would love to receive a shoutout and/or your feedback ;)

Installation

To install ExUnited, please add ex_united to your list of dependencies in mix.exs:

def deps do
  [
    {:ex_united, "~> 0.1.5", only: :test}
  ]
end

Replace the default ExUnit.start() invocation in the test helper file with ExUnited.start():

# test/test_helper.exs
ExUnited.start()

Explicitly start ExUnit yourself

As of version 0.1.2, you can also start ExUnit yourself explicitly and add ExUnited.start(false) instead:

# test/test_helper.exs
ExUnit.start()
ExUnited.start(false)

ATTENTION: When also using meck-based packages

The following errors can occur when also using packages like mock or MecksUnit (which both use the Erlang library meck to mock functions) and spawning the nodes with the default environment test:

  • (UndefinedFunctionError) function Some.Module.some_function/1 is undefined
  • (ErlangError) Erlang error: {{:undefined_module, < Some.Module >}

To tackle this, you should configure any other (Mix) environment to spawn the nodes with. Configure it like so:

# config/test.exs
import Config

config :ex_united,
  mix_env: :dev

You might also want to consider using a bogus environment (e.g. :void) to skip the non-relevant :dev dependencies, like credo or dialyxir probably. That will save some compile time.

And last but not least, please note that using a different environment within CI builds will require compiling the project in that particular environment on beforehand of the tests. Otherwise spawning the nodes will take too much time and that will cause timeout errors during the tests.

# .gitlab-ci.yml
before_script:
  ...
  - MIX_ENV=void mix deps.get
  - MIX_ENV=void mix run -e 'IO.puts("Done.")'
  - epmd -daemon
script:
  - mix test

Usage

For using ExUnited, the two essential functions are:

  1. ExUnited.spawn/2 - Spawns (Mix.Config configured, additional code loaded, supervising) nodes
  2. ExUnited.teardown/0 - Kills the spawned nodes and it also cleans up their generated files

The most simplest setup

Nodes can be specified as a list of atoms, just like in the following example. Their node names will be :"[email protected]" and :"[email protected]" respectively).

Please do not forget to invoke ExUnited.teardown/0 at the on_exit hook.

setup do
  {:ok, spawned} = ExUnited.spawn([:bruce, :clark])

  on_exit(fn ->
    ExUnited.teardown()
  end)

  spawned
end

"Partially versus Fully connected" and/or "Verbose" spawned nodes

As a second argument, you can pass a list of atoms for the options:

  • :connect - if true a "fully connected" node will be spawned (see the erl -connect_all flag for more information). Defaults to false
  • :verbose - if true the STDOUT of the spawned node will be printed. Defaults to false

See ExUnited.spawn/2 for more information.

setup do
  {:ok, spawned} = ExUnited.spawn([:roy], [:connect, :verbose])

  on_exit(fn ->
    ExUnited.teardown()
  end)

  spawned
end

Which results in the following when running tests:

PME-Legend ~/S/ex_united:master> mix test test/ex_united/supervised_test.exs:140
Excluding tags: [:test]
Including tags: [line: "140"]

iex([email protected])> Compiling 1 file (.ex)
iex([email protected])> Generated void app
iex([email protected])> Interactive Elixir (1.10.1) - press Ctrl+C to exit (type h() ENTER for help)
iex([email protected])1>
.

Finished in 0.9 seconds
2 tests, 0 failures, 1 excluded

Exclude certain dependencies for all spawned nodes

You can exclude certain (Mix) dependencies for ALL spawned nodes by for instance adding exclude: [:inch_ex] to the options. This can significantly improve the speed of your tests.

setup do
  {:ok, spawned} = ExUnited.spawn([:bruce, :clark], [:verbose, exclude: [:inch_ex]])

  on_exit(fn ->
    ExUnited.teardown()
  end)

  spawned
end

The following dependencies are excluded by default:

  • :credo
  • :dialyxir
  • :ex_doc
  • :ex_united
  • :excoveralls

Configuring the spawned nodes

Aside from the list of atoms, you can also specify nodes as a keyword list in case you want to configure them. The following options are available:

  • :code_paths - a list of directories that will be included
  • :exclude - a list of dependencies that will be excluded
  • :supervise - the child spec(s) used for supervisioning

Including additional code

It would be a best practice to create a directory called test/nodes in which you put a directory containing code for a specific spawned node. Please note that the file called config.exs is supported for Mix.Config:

setup do
  {:ok, spawned} =
    ExUnited.spawn(
      eric: [
        code_paths: [
          "test/nodes/cantona"
        ]
      ]
    )

  on_exit(fn ->
    ExUnited.teardown()
  end)

  spawned
end

See test/ex_united/supervised_test.exs with its corresponding test/nodes/ronaldo as an example.

Exclude certain dependencies for a specific spawned node

Add the :exclude list as follows:

setup do
  {:ok, spawned} =
    ExUnited.spawn(
      bruce: [
        code_paths: [
          "test/nodes/bruce"
        ],
        exclude: [
          :my_unused_dependency,
          :my_current_project
        ],
        supervise: [MyAwesomeGenServer]
      ],
      clark: [
        code_paths: [
          "test/nodes/clark"
        ],
        supervise: [MyOtherAwesomeGenServer]
      ]
    )

  on_exit(fn ->
    ExUnited.teardown()
  end)

  spawned
end

Add supervisioning

Childspecs should be the same argument as if you are adding them to your classic <app>/application.ex file:

setup do
  {:ok, spawned} =
    ExUnited.spawn(
      bruce: [
        code_paths: [
          "test/nodes/bruce"
        ],
        supervise: [MyAwesomeGenServer]
      ],
      clark: [
        code_paths: [
          "test/nodes/clark"
        ],
        supervise: [MyOtherAwesomeGenServer]
      ]
    )

  on_exit(fn ->
    ExUnited.teardown()
  end)

  spawned
end

Pay attention that functions within childspecs should be quoted.

setup do
  {:ok, spawned} =
    ExUnited.spawn(
      [
        roy: [
          code_paths: [
            "test/nodes/keane"
          ],
          supervise: [
            {
              Roy,
              talk:
                quote do
                  fn
                    1 -> "Hi, I am Roy Keane"
                    2 -> "I am keen as mustard"
                    3 -> "I like to be peachy keen"
                  end
                end
            }
          ]
        ]
      ],
      [:verbose]
    )

  on_exit(fn ->
    ExUnited.teardown()
  end)

  spawned
end

Easily assert and refute within the context of spawned nodes

To seemlessly execute assertions and refutations within spawned nodes, you can setup your test module by either using ExUnited.Case instead of ExUnit.Case:

defmodule MyNodesTest do
  use ExUnited.Case
end

Or by importing the ExUnited.Case module:

defmodule MyNodesTest do
  use ExUnit.Case
  import ExUnited.Case
end

Writing assertions and refutations within the context of a certain spawned is pretty straight forward with the use of the ExUnited.Case.as_node/2 function as if you are writing your class assert and/or refute statements:

defmodule MyNodesTest do
  use ExUnited.Case

  setup do
    {:ok, spawned} = ExUnited.spawn([:bruce, :clark])

    on_exit(fn ->
      ExUnited.teardown()
    end)

    spawned
  end

  test "assertions and refutations within node contexts", spawned do
    bruce = get_in(spawned, [:bruce, :node])

    as_node(bruce) do
      assert :"[email protected]" = Node.self()
      refute :"[email protected]" == Node.self()
    end

    as_node(:clark) do
      assert :"[email protected]" = Node.self()
      refute :"[email protected]" == Node.self()
    end
  end
end

See ExUnited.Case.as_node/2 for more information.

Contact me

For support, remarks and requests, please mail me at [email protected].

License

Copyright (c) 2020 Paul Engel, released under the MIT License

http://github.com/archan937http://twitter.com/archan937[email protected]

Permission 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:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE 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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].