### Typical Mock Setup with Hammox Source: https://github.com/msz/hammox/blob/master/README.md Demonstrates a typical unit test setup using Hammox to mock a database behavior. It defines a mock, sets expectations for a function call, and asserts the result. ```elixir defmodule StatsTest do use ExUnit.Case, async: true import Hammox test "count_users/0 returns correct user count" do defmock(DatabaseMock, for: Database) expect(DatabaseMock, :get_users, fn -> ["joe", "jim"] end) assert 2 == Stats.count_users(DatabaseMock) end end ``` -------------------------------- ### Using `setup_all` with `Hammox.protect/2` Source: https://github.com/msz/hammox/blob/master/README.md Shows how to integrate `Hammox.protect/2` within an `ExUnit.Case`'s `setup_all` hook for cleaner test setup. The protected function is stored in the test context and accessed in the test function. ```elixir defmodule RealDatabaseTest do use ExUnit.Case, async: true setup_all do %{get_users_0: Hammox.protect({RealDatabase, :get_users, 0}, Database)} end test "get_users/0 returns list of users", %{get_users_0: get_users_0} do assert {:ok, ["real-jim", "real-joe"]} == get_users_0.() end end ``` -------------------------------- ### Attaching Telemetry Handlers Source: https://github.com/msz/hammox/blob/master/guides/Telemetry.md Example Elixir code demonstrating how to attach telemetry handlers for start, stop, and exception events in Hammox. It defines a helper function `build_events` to generate event lists and uses `Telemetry.attach_many/4` to register handlers. ```elixir def build_events(event_atom) do event_list = [ :expect, :allow, :run_expect, :check_call, :match_args, :match_return_value, :fetch_typespecs, :cache_put, :stub, :verify_on_exit! ] Enum.map(event_list, fn event -> [:hammox, event, event_atom] end) end ... other appplication.ex code here start_events = build_events(:start) :ok = :telemetry.attach_many( "hammox-start", start_events, &handle_event/4, nil ) stop_events = .build_events(:stop) :ok = :telemetry.attach_many( "hammox-stop", stop_events, &handle_event/4, nil ) exception_events = .build_events(:exception) :ok = :telemetry.attach_many( "hammox-exception", exception_events, &handle_event/4, nil ) ``` -------------------------------- ### Hammox Installation Source: https://github.com/msz/hammox/blob/master/README.md Instructions for adding Hammox to your Elixir project's dependencies in `mix.exs`. It replaces Mox if already in use. ```elixir def deps do [ {:hammox, "~> 0.7", only: :test} ] end ``` -------------------------------- ### Handle Hammox Expect Telemetry Events in Elixir Source: https://github.com/msz/hammox/blob/master/guides/Telemetry.md This Elixir module, HammoxTelemetryHandler, processes telemetry events related to Hammox expectations. It starts spans for 'start' events, adding relevant tags like mock name, function name, and expect count. It also handles 'stop' and 'exception' events to update and finish spans, including error reporting. ```elixir defmodule HammoxTelemetryHandler do alias Spandex.Tracer ... def handle_event([:hammox, :expect, :start], measurements, metadata, _config) when is_map(measurements) do mock_name = Map.get(metadata, :mock) func_name = Map.get(metadata, :name) expect_count = Map.get(metadata, :count) |> to_string tags = [] |> tags_put(:mock, mock_name) |> tags_put(:func_name, func_name) |> tags_put(:expect_count, expect_count) system_time = get_time(measurements, :system_time) if Tracer.current_trace_id() do span_string = "#{mock_name}.#{func_name}" |> String.trim_leading("Elixir.") span_string = "expect #{span_string}" _span_context = Tracer.start_span(span_string, service: :hammox, tags: tags) Tracer.update_span(start: system_time) end end def handle_event([:hammox, :expect, :stop], measurements, _metadata, _config) do handle_exception(measurements) end def handle_event([:hammox, :expect, :exception], measurements, _metadata, _config) do handle_exception(measurements) end defp handle_exception(measurements) do error_message = "Exception occurred during hammox execution" Logger.error(error_message) if Tracer.current_trace_id() do current_span = Tracer.current_span([]) Tracer.update_span_with_error(error_message, current_span) end handle_stop(measurements) end defp handle_stop(measurements, tags \ []) do duration_time = get_time(measurements, :duration) case Tracer.current_span([]) do %{start: start_time} -> completion_time = start_time + duration_time Tracer.update_span(tags: tags, completion_time: completion_time) Tracer.finish_span() _no_current_span -> :ok end end defp get_time(log_entry, key) do log_entry |> Map.get(key) end end ``` -------------------------------- ### Contract Break Detection with Hammox Source: https://github.com/msz/hammox/blob/master/README.md Illustrates how Hammox detects contract violations when a mock's return value does not match the behavior's typespec. This example shows a `Hammox.TypeMatchError`. ```elixir defmodule Database do @callback get_users() :: {:ok, [binary()]} | {:error, term()} end ``` ```none ** (Hammox.TypeMatchError) Returned value ["joe", "jim"] does not match type {:ok, [binary()]} | {:error, term()}. ``` -------------------------------- ### Hammox Telemetry Events Source: https://github.com/msz/hammox/blob/master/guides/Telemetry.md This section details all supported telemetry events within Hammox, categorized into 'Start Events', 'Stop Events', and 'Exception Events'. Each event is listed with its corresponding metadata, providing insights into the data captured during different testing phases. ```APIDOC Start Events: - [:hammox, :expect, :start] - metadata: - `mock`: Name of the mock/behaviour - `function_name`: Name of the function that is being mocked - `count`: Total expect count (defaults to 1) - [:hammox, :allow, :start] - metadata: - `mock`: Name of the mock/behaviour - `owner_pid`: PID of the process that owns the mock - `allowed_via`: PID of the process that is requesting allowance of the mock - [:hammox, :run_expect, :start] - metadata: none - [:hammox, :check_call, :start] - metadata: none - [:hammox, :match_args, :start] - metadata: none - [:hammox, :match_return_value, :start] - metadata: none - [:hammox, :fetch_typespecs, :start] - metadata: - `behaviour_name`: Name of behaviour to fetch the typespec for - `function_name`: Name of the function to fetch the typespec for - `arity`: Arity of the function to fetch the typespec for - [:hammox, :cache_put, :start] - metadata: - `key`: Key of the value to put in cache - `value`: Value to put in cache - [:hammox, :stub, :start] - metadata: - `mock`: Name of the mock to stub - `function_name`: Name of the function to stub on the mock - [:hammox, :verify_on_exit!, :start] - metadata: - `context`: Context passed into `verify_on_exit!` setup function Stop Events: - [:hammox, :expect, :stop] - metadata: - `mock`: Name of the mock/behaviour - `func`: Name of the function that is being mocked - `count`: Total expect count (defaults to 1) - [:hammox, :allow, :stop] - metadata: none - [:hammox, :run_expect, :stop] - metadata: none - [:hammox, :check_call, :start] - metadata: - `total_specs_checked`: Count of total specs checked during verification of arguments and return values - [:hammox, :match_args, :stop] - metadata: none - [:hammox, :match_return_value, :stop] - metadata: none - [:hammox, :fetch_typespecs, :stop] - metadata: none - [:hammox, :cache_put, :stop] - metadata: none - [:hammox, :stub, :stop] - metadata: none - [:hammox, :verify_on_exit!, :stop] - metadata: none Exception Events: - [:hammox, :expect, :exception] - metadata: none - [:hammox, :allow, :exception] - metadata: none - [:hammox, :run_expect, :exception] - metadata: none - [:hammox, :check_call, :start] - metadata: none - [:hammox, :match_args, :exception] - metadata: none - [:hammox, :match_return_value, :exception] - metadata: none - [:hammox, :fetch_typespecs, :exception] - metadata: none - [:hammox, :cache_put, :exception] - metadata: none - [:hammox, :stub, :exception] - metadata: none - [:hammox, :verify_on_exit!, :exception] - metadata: none ``` -------------------------------- ### Using `Hammox.protect/2` with Module and Behavior Source: https://github.com/msz/hammox/blob/master/README.md Illustrates a convenient way to use `Hammox.protect/2` within `setup_all` when both the implementation module and behavior module are provided. This automatically creates decorated functions for all behavior callbacks. ```elixir defmodule RealDatabaseTest do use ExUnit.Case, async: true setup_all do Hammox.protect(RealDatabase, Database) end test "get_users/0 returns list of users", %{get_users_0: get_users_0} do assert {:ok, ["real-jim", "real-joe"]} == get_users_0.() end end ``` -------------------------------- ### Using `use Hammox.Protect` Macro Source: https://github.com/msz/hammox/blob/master/README.md Demonstrates using the `use Hammox.Protect` macro to locally define protected versions of functions. This approach simplifies tests by allowing direct calls to the protected functions as if they were imported. ```elixir defmodule RealDatabaseTest do use ExUnit.Case, async: true use Hammox.Protect, module: RealDatabase, behaviour: Database test "get_users/0 returns list of users" do assert {:ok, ["real-jim", "real-joe"]} == get_users() end end ``` -------------------------------- ### Protecting Real Implementation Functions Source: https://github.com/msz/hammox/blob/master/README.md Demonstrates how to use `Hammox.protect/2` to create a protected version of a real module function. This protected function can then be used in tests to assert its behavior against a defined behavior. ```elixir defmodule RealDatabaseTest do use ExUnit.Case, async: true test "get_users/0 returns list of users" do get_users_0 = Hammox.protect({RealDatabase, :get_users, 0}, Database) assert {:ok, ["real-jim", "real-joe"]} == get_users_0.() end end ``` -------------------------------- ### Enable Telemetry Configuration Source: https://github.com/msz/hammox/blob/master/guides/Telemetry.md Configuration snippet to enable telemetry reporting in Hammox by setting the `enable_telemetry?` option to `true` in your application's configuration. ```elixir config :hammox, enable_telemetry?: true ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.