Supertester.GenServerHelpers (supertester v0.1.0)

View Source

Specialized helpers for GenServer testing patterns.

This module provides utilities specifically designed for testing GenServer behavior, including state management, concurrent testing, and error scenarios.

Key Features

  • Safe GenServer state access
  • Cast and sync patterns for proper async operation testing
  • Concurrent testing utilities
  • Error testing and crash recovery
  • Timeout-aware GenServer operations

Usage

import Supertester.GenServerHelpers

test "genserver state management" do
  {:ok, server} = setup_isolated_genserver(MyGenServer)
  state = get_server_state_safely(server)
  assert state.counter == 0
end

Summary

Functions

Makes a GenServer call with timeout handling.

Sends a cast and then synchronizes with a sync message.

Performs concurrent calls to a GenServer for stress testing.

Safely retrieves the internal state of a GenServer.

Performs stress testing on a GenServer with various operations.

Tests how a GenServer handles invalid messages.

Tests GenServer crash recovery behavior.

Functions

call_with_timeout(server, message, timeout \\ 1000)

@spec call_with_timeout(GenServer.server(), term(), timeout()) ::
  {:ok, term()} | {:error, term()}

Makes a GenServer call with timeout handling.

Parameters

  • server - The GenServer pid or name
  • message - The message to send
  • timeout - Timeout in milliseconds (default: 1000)

Returns

{:ok, response} if successful, {:error, reason} otherwise

Example

{:ok, response} = call_with_timeout(server, :get_counter, 5000)

cast_and_sync(server, cast_message, sync_message \\ :__supertester_sync__)

@spec cast_and_sync(GenServer.server(), term(), term()) ::
  :ok | {:ok, term()} | {:error, term()}

Sends a cast and then synchronizes with a sync message.

This is crucial for testing async operations - it ensures the cast has been processed before the test continues.

Parameters

  • server - The GenServer pid or name
  • cast_message - The async message to cast
  • sync_message - The sync message for synchronization (default: :supertester_sync)

Returns

:ok if successful, {:error, reason} otherwise

Example

:ok = cast_and_sync(server, {:increment, 5})
{:ok, state} = get_server_state_safely(server)
assert state.counter == 5

concurrent_calls(server, calls, count \\ 10)

@spec concurrent_calls(GenServer.server(), [term()], pos_integer()) ::
  {:ok, [{term(), [term()]}]}

Performs concurrent calls to a GenServer for stress testing.

Parameters

  • server - The GenServer pid or name
  • calls - List of messages to send concurrently
  • count - Number of concurrent processes per message (default: 10)

Returns

{:ok, results} with list of {call, result} tuples

Example

calls = [:get_counter, {:increment, 1}, :get_counter]
{:ok, results} = concurrent_calls(server, calls, 5)

get_server_state_safely(server)

@spec get_server_state_safely(GenServer.server()) :: {:ok, term()} | {:error, term()}

Safely retrieves the internal state of a GenServer.

This function attempts to get the GenServer state without causing crashes if the server is not responsive or doesn't support state inspection.

Parameters

  • server - The GenServer pid or name

Returns

{:ok, state} if successful, {:error, reason} otherwise

Example

{:ok, state} = get_server_state_safely(my_server)

stress_test_server(server, operations, duration \\ 5000)

@spec stress_test_server(GenServer.server(), [term()], pos_integer()) :: {:ok, map()}

Performs stress testing on a GenServer with various operations.

Parameters

  • server - The GenServer pid or name
  • operations - List of operations to perform randomly
  • duration - Duration in milliseconds (default: 5000)

Returns

{:ok, stats} with stress test statistics

Example

operations = [
  {:call, :get_counter},
  {:cast, {:increment, 1}},
  {:call, {:add, 5}}
]
{:ok, stats} = stress_test_server(server, operations, 10_000)

test_invalid_messages(server, invalid_messages)

@spec test_invalid_messages(GenServer.server(), [term()]) :: {:ok, [{term(), term()}]}

Tests how a GenServer handles invalid messages.

Parameters

  • server - The GenServer pid or name
  • invalid_messages - List of invalid messages to test

Returns

{:ok, results} with test results for each message

Example

invalid_messages = [:invalid_call, {:unknown, :message}, "string_message"]
{:ok, results} = test_invalid_messages(server, invalid_messages)

test_server_crash_recovery(server, crash_reason)

@spec test_server_crash_recovery(GenServer.server(), term()) ::
  {:ok, map()} | {:error, term()}

Tests GenServer crash recovery behavior.

Parameters

  • server - The GenServer pid or name
  • crash_reason - The reason to crash the server with

Returns

{:ok, recovery_info} if recovery successful, {:error, reason} otherwise

Example

{:ok, info} = test_server_crash_recovery(server, :test_crash)
assert info.recovered == true