PactEx.Matchers (pact_ex v0.1.0)

Copy Markdown View Source

Pact matchers.

Each function returns a map in the pact_ffi "integration JSON" format. Put the map where you would put a plain value, in a body, header, query value or path. The mock server matches by rule instead of by equality, and the example value is what it returns to the consumer.

import PactEx.Matchers

%{
  "id" => integer(1),
  "name" => like("Ada"),
  "email" => regex("ada@example.com", ".+@.+"),
  "tags" => each_like(like("admin"), min: 1)
}

Matchers nest, and the results are plain maps that encode to JSON.

Summary

Functions

Matches any boolean. value is the example.

Matches a date or time string in the given Java DateTimeFormatter format.

Matches any decimal number. value is the example and must be a float.

Matches a map whose keys all match the regular expression pattern.

Matches a list in which every element looks like value.

Matches any string that contains substring. substring is also the example.

Matches any integer. value is the example.

Matches any value of the same type as value.

Matches a value against the regular expression pattern. The value is usually a string.

Types

t()

@type t() :: %{required(String.t()) => term()}

Functions

boolean(value)

@spec boolean(boolean()) :: t()

Matches any boolean. value is the example.

iex> PactEx.Matchers.boolean(true)
%{"pact:matcher:type" => "boolean", "value" => true}

datetime(value, format)

@spec datetime(String.t(), String.t()) :: t()

Matches a date or time string in the given Java DateTimeFormatter format.

value is the example string. It should follow format.

iex> PactEx.Matchers.datetime("2026-10-22", "yyyy-MM-dd")
%{"pact:matcher:type" => "datetime", "value" => "2026-10-22", "format" => "yyyy-MM-dd"}

decimal(value)

@spec decimal(float()) :: t()

Matches any decimal number. value is the example and must be a float.

Integers are rejected: pact_matching checks decimals with a float test, and an integer such as 2 encodes as 2, which fails its own rule. Use 2.0.

iex> PactEx.Matchers.decimal(1.5)
%{"pact:matcher:type" => "decimal", "value" => 1.5}

each_key(value, pattern)

@spec each_key(map(), String.t()) :: t()

Matches a map whose keys all match the regular expression pattern.

value is the example map. Raises FunctionClauseError if it is not a map.

iex> PactEx.Matchers.each_key(%{"happy" => true}, "(happy|ferocious)")
%{
  "pact:matcher:type" => "eachKey",
  "value" => %{"happy" => true},
  "rules" => [%{"pact:matcher:type" => "regex", "regex" => "(happy|ferocious)"}]
}

each_like(value, opts \\ [min: 1])

@spec each_like(term(), [{:min, pos_integer()}]) :: t()

Matches a list in which every element looks like value.

value is the example element. The list needs at least :min elements (default 1). The example list repeats value :min times, because the mock server does not pad it and would reject a shorter example.

Raises ArgumentError if :min is not a positive integer, or if opts has any key other than :min. Raises FunctionClauseError if opts is not a list.

iex> PactEx.Matchers.each_like("x", min: 2)
%{"pact:matcher:type" => "type", "value" => ["x", "x"], "min" => 2}

include(substring)

@spec include(String.t()) :: t()

Matches any string that contains substring. substring is also the example.

iex> PactEx.Matchers.include("ell")
%{"pact:matcher:type" => "include", "value" => "ell"}

integer(value)

@spec integer(integer()) :: t()

Matches any integer. value is the example.

iex> PactEx.Matchers.integer(1)
%{"pact:matcher:type" => "integer", "value" => 1}

like(value)

@spec like(term()) :: t()

Matches any value of the same type as value.

iex> PactEx.Matchers.like(1)
%{"pact:matcher:type" => "type", "value" => 1}

regex(value, pattern)

@spec regex(term(), String.t()) :: t()

Matches a value against the regular expression pattern. The value is usually a string.

value is the example value. It should match pattern.

value is typed term() and not guarded, because a regex rule also applies to values that are not strings. In a JSON body, pact_matching matches the pattern against the string form of a number, a boolean or null (the text it has in JSON: 42, 1.5, true, null), so regex(42, "\d+") is valid. For a list or a map example, the pattern applies to each scalar inside it, not to the JSON text of the whole list or map. Path and query values are matched as strings.

iex> PactEx.Matchers.regex("male", "(male|female)")
%{"pact:matcher:type" => "regex", "value" => "male", "regex" => "(male|female)"}