Skip to content

Instantly share code, notes, and snippets.

@ishikawa
Last active July 27, 2020 12:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ishikawa/c7c7ec9123bf17b6b7f9cf7256e947f4 to your computer and use it in GitHub Desktop.
Save ishikawa/c7c7ec9123bf17b6b7f9cf7256e947f4 to your computer and use it in GitHub Desktop.
submap?/3 for Elixir's map. It's useful for testing API response (see submap_usage_test.exs).
defmodule MapHelpers do
@doc """
Checks if `map1`'s entries are all contained in `map2`.
## Options
- `:recursive` - Recursively checks if a value is map (Default: `false`)
## Examples
```
iex> submap?(%{}, %{})
true
iex> submap?(%{a: 1, b: 2}, %{a: 1, b: 2, c: 3})
true
iex> submap?(%{a: 1, b: 2, c: 3}, %{a: 1, b: 2})
false
iex> submap?(%{a: 1, b: %{}}, %{a: 1, b: %{c: 1}})
false
iex> submap?(%{a: 1, b: %{}}, %{a: 1, b: %{c: 1}}, recursive: true)
true
```
"""
@spec submap?(map, map, [option]) :: boolean when option: {:recursive, boolean}
def submap?(map1, map2, opts \\ []) when is_map(map1) and is_map(map2) do
recursive = Keyword.get(opts, :recursive, false)
map1
|> Enum.all?(fn
{k, v} when is_map(v) and recursive ->
m = map2[k]
is_map(m) && submap?(v, m)
{k, v} ->
map2[k] === v
end)
end
@doc """
Equivalent to `submap?(map1, map2, recursive: true)`.
"""
@spec deep_submap?(map, map) :: boolean
def deep_submap?(map1, map2), do: submap?(map1, map2, recursive: true)
end
defmodule MyApp.SubmapUsageTest do
use MyApp.ConnCase
import MapHelpers, only: [deep_submap?: 2]
test "API response match test", %{conn: conn, user: user} do
conn =
conn
|> get("/users/#{user.id}")
# response = %{
# "id": "1772493858641",
# "name": "alice",
# "profile": %{
# "icon" => "http://avatar.example.com/users/1772493858641",
# "description" => "...(long description)"
# }
# }
assert response = json_response(conn, :ok)
# Write a helper function that returns an exmaple response, and
# we can check the response matches an example with `deep_submap?/2`.
assert deep_submap?(user_response(user), response)
end
defp user_response(user) do
# We don't want to check `profile.description`, so omit the field.
%{
"id": to_string(user.id),
"name": user.name,
"profile" => %{
"icon" => "http://avatar.example.com/users/#{user.id}"
}
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment