AB (AB v0.2.1)
View SourceA macro-based property testing generator that analyzes function typespecs to automatically generate appropriate test data and output validators.
Usage
defmodule MyModuleTest do
use ExUnit.Case
use ExUnitProperties
import AB
# Generate property test for a function with typespec
property_test MyModule, :my_function
end
Summary
Functions
Macro that benchmarks two functions with identical typespecs using Benchee.
Macro that compares two functions with identical typespecs using the same generated test data.
Creates input generators from type specifications. Optionally accepts a module to resolve user-defined type aliases.
Creates invalid input generators from type specifications.
Creates output validators from type specifications.
Creates a struct generator from @type definition.
Formats a typespec AST into a human-readable string using Elixir's standard functions where possible.
Extracts the typespec for a given function.
Parses a spec into input and output types.
Macro that generates property testing utilities for a function based on its typespec.
Macro that generates robustness tests for a function based on its typespec.
Runs a property test and returns detailed failure information including actual values.
Analyzes a failed test using runtime values and suggests a corrected typespec.
Compares two type specifications for equivalence.
Macro that generates property tests for all public functions in a module.
Macro that validates struct type definitions against function typespecs.
Validates type consistency between @type definitions and @spec definitions.
Functions
Macro that benchmarks two functions with identical typespecs using Benchee.
Takes two module/function pairs, verifies their typespecs are identical, and if so, runs a benchmark comparison using generated test data.
Example
# Benchmark two sorting implementations
benchmark_test {AB, :a}, {AB, :b}
# With custom options
benchmark_test {AB, :a}, {AB, :b}, time: 5, memory_time: 2This will generate a benchmark test that compares performance of both functions.
Macro that compares two functions with identical typespecs using the same generated test data.
Takes two module/function pairs, verifies their typespecs are identical, and if so, runs both functions on the same generated inputs to ensure they produce identical outputs.
Example
# Compare two sorting implementations
compare_test {AB, :a}, {AB, :b}
# With verbose output
compare_test {AB, :a}, {AB, :b}, verbose: trueThis will generate a property test that validates both functions behave identically.
@spec create_input_generator_runtime([any()], module() | nil) :: Enumerable.t()
Creates input generators from type specifications. Optionally accepts a module to resolve user-defined type aliases.
@spec create_invalid_input_generator_runtime([any()], module() | nil) :: Enumerable.t()
Creates invalid input generators from type specifications.
Creates output validators from type specifications.
Creates a struct generator from @type definition.
Formats a typespec AST into a human-readable string using Elixir's standard functions where possible.
Extracts the typespec for a given function.
Parses a spec into input and output types.
Macro that generates property testing utilities for a function based on its typespec.
Takes a module and function name, analyzes the typespec, and generates:
- Input generators based on the function's parameter types
- Output validators based on the function's return type
- A complete property test
Example
# For a function with spec: @spec sort_list([integer()]) :: [integer()]
property_test MyModule, :sort_list
# With verbose output
property_test MyModule, :sort_list, verbose: trueThis will generate a property test that validates the function behavior.
Macro that generates robustness tests for a function based on its typespec.
Takes a module and function name, analyzes the typespec, and generates:
- Invalid input generators that create data NOT matching the function's parameter types
- Tests that verify functions either raise errors or return invalid output when given invalid input
- Ensures functions fail gracefully rather than silently accepting wrong input types
Example
# For a function with spec: @spec process(integer()) :: string()
robust_test MyModule, :process
# With verbose output
robust_test MyModule, :process, verbose: trueThis will generate tests that verify the function properly rejects invalid input.
@spec run_property_test_with_details(module(), atom(), keyword()) :: {:ok, integer()} | {:error, map()}
Runs a property test and returns detailed failure information including actual values.
This is used by the mix task to capture failure details for typespec suggestions. Returns {:ok, count} on success or {:error, failure_details} on failure.
@spec suggest_typespec_correction(module(), atom(), list(), any(), atom()) :: %{ old_spec: String.t(), new_spec: String.t(), old_spec_ast: {[any()], any()}, new_spec_ast: {[any()], any()}, reason: String.t() } | {:error, String.t()}
Analyzes a failed test using runtime values and suggests a corrected typespec.
Takes actual runtime values (inputs and result) along with the current typespec, infers the correct types from the values, and suggests a corrected typespec.
Parameters
module: The module containing the functionfunction_name: The name of the function that failedactual_inputs: List of actual input values used in the testactual_result: The actual result value returned by the functionmismatch_type: Either:returnor:argumentto indicate which type mismatched
Returns
A map containing:
:old_spec- The current typespec as a string:new_spec- The suggested corrected typespec as a string:old_spec_ast- The current typespec as AST {input_types, output_type}:new_spec_ast- The suggested typespec as AST {input_types, output_type}:reason- Why the typespec needs correction
Example
iex> AB.suggest_typespec_correction(NumberFunctions, :double, [1.0], 2.0, :return)
%{
old_spec: "@spec double(number()) :: binary()",
new_spec: "@spec double(number()) :: float()",
old_spec_ast: {[{:type, 0, :number, []}], {:type, 0, :binary, []}},
new_spec_ast: {[{:type, 0, :number, []}], {:type, 0, :float, []}},
reason: "Return type mismatch: function returns float() but typespec declares binary()"
}
Compares two type specifications for equivalence.
Macro that generates property tests for all public functions in a module.
Takes a module and automatically generates property tests for all exported functions that have typespecs defined. This is useful for validating an entire module's API.
Example
# Validate all public functions in a module
validate_module MyModule
# With verbose output
validate_module MyModule, verbose: trueThis will generate property tests for each public function with a typespec.
Macro that validates struct type definitions against function typespecs.
This catches inconsistencies where @type definitions don't match @spec definitions.
Example
# This will fail if @type t :: %AB{a: atom()} but @spec expects integer()
validate_struct_consistency AB
Validates type consistency between @type definitions and @spec definitions.