Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/campezzi/ignorant

Simplify comparison of Elixir data structures by ensuring fields are present but ignoring their values.
https://github.com/campezzi/ignorant

Last synced: about 2 months ago
JSON representation

Simplify comparison of Elixir data structures by ensuring fields are present but ignoring their values.

Lists

README

        

# Ignorant

Because sometimes ignorance is bliss.

Ignorant defines a protocol for data structures that may selectively ignore specific values they
contain, usually to simplify partial comparison in tests.

## Motivation
Sometimes it's useful to compare data structures in tests while ignoring pieces of data that are not
deterministic - auto-generated primary keys, calculated timestamps, and so on. Ignoring certain bits
ensures the _shape_ of the data is correct when it's not possible to enforce its values.

## Example

An implementation for `Map` is provided with that package for the purpose described above. Suppose
you have an API endpoint that fetches a record from the database and returns JSON data which
translates to this `response` map:

```elixir
%{
id: 37,
name: "Jim"
}
```

Now let's say you want to write a test to ensure that it returns the right data. Problem is, the
`id` field is auto-generated and therefore has no deterministic value between test runs. You could
use pattern matching...

```elixir%{a}
assert %{id: _, name: "Jim"} = response
```

...but you can't assign the value on the left side to a variable (or module attribute) and reuse it,
and if the match fails you don't get a nice diff message. That's where `Ignorant` comes in:

```elixir
assert %{id: :ignored, name: "Jim"} == Ignorant.ignore(response, [:id])
```

The left side is now a proper value that can be put in a variable or attribute and reused. Also note
the `==` (equality) assertion instead of `=` (match) assertion. That gives us a pretty diff that
highlights exactly what doesn't look the same on both sides, and is also stricter (which is a good
idea in tests).

It quickly becomes annoying and error-prone to tag things as `:ignored` in one side of the
comparison and include the corresponding field on the second parameter in the call to `ignore/2` on
the other side, so we can clean that up by using `merge_ignored/2`:

```elixir
expected = %{id: :ignored, name: "Jim"}
assert expected == Ignorant.merge_ignored(response, expected)
```

`merge_ignored/2` will walk through the `expected` map extracting all fields tagged as `:ignored`, and
then apply those to `response` while keeping all other values intact. The resulting map should be
equal to `expected`. If not, you'll get a pretty diff explaining what's different.

## Installation

1. Add `ignorant` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[{:ignorant, "~> 0.1.0"}]
end
```

2. Ensure `ignorant` is started before your application:

```elixir
def application do
[applications: [:ignorant]]
end
```