StreamData

repository·main·Indexed 21 days ago

https://github.com/whatyouhide/stream_data

An Elixir library for data generation and property-based testing. It provides the StreamData module for creating randomized data streams that implement the Enumerable protocol, and the ExUnitProperties module for verifying code properties by injecting randomized inputs to attempt to falsify assertions.

Tokens
970
Snippets
3
Records
5
Agent score
26%

What's inside stream_data

  1. Compare StreamData with PropCheck/PropEr

    main

    When choosing between StreamData and PropCheck (a wrapper for Erlang's PropEr), consider these differences:

    Choose PropCheck if:

    • You require stateful property-based testing (testing a system by building a model of its state).
    • You need the ability to store counter-examples in a file (StreamData requires reusing the failure seed to reproduce issues).

    Choose StreamData if:

    • You want native Elixir implementation with an idiomatic Elixir API (generators are Enumerable).
    • You want to use generators for general data generation outside of testing (e.g., as standard Elixir streams).
  2. How StreamData components work together

    main

    StreamData is composed of two main functional areas:

    1. Data Generation: Handled by the StreamData module. It provides "generators" that implement the Enumerable protocol, allowing them to be used as infinite streams of data in standard Elixir code.
    2. Property-based Testing: Handled by the ExUnitProperties module. This uses the generators from StreamData to attempt to falsify properties (assertions that should hold true for a set of data) by injecting randomized inputs.
  3. Perform property-based testing with ExUnitProperties

    main

    To perform property-based testing, use the ExUnitProperties module. Instead of testing specific hardcoded inputs, you define a property and use the check all syntax to bind variables to generators. The framework will then attempt to find inputs that falsify your assertions.

    require ExUnitProperties
    use ExUnitProperties
    
    property "bin1 <> bin2 always starts with bin1" do
      check all bin1 <- binary(),
                bin2 <- binary() do
        assert String.starts_with?(bin1 <> bin2, bin1)
      end
    end
  4. Generate data using the StreamData module

    main

    The StreamData module provides generators for various data types. Because generators implement Enumerable, you can use them with Stream and Enum functions. You can also create complex custom generators using ExUnitProperties.gen all to combine existing generators with logic and constraints.

    # Using a basic generator as a stream
    StreamData.integer() |> Stream.map(&abs/1) |> Enum.take(3)
    #=> [1, 0, 2]
    
    # Creating a custom complex generator
    require ExUnitProperties
    
    domains = ["gmail.com", "hotmail.com", "yahoo.com"]
    
    email_generator = 
      ExUnitProperties.gen all name <- StreamData.string(:alphanumeric), 
                               name != "", 
                               domain <- StreamData.member_of(domains) do
        name <> "@" <> domain
      end
    
    Enum.take(StreamData.resize(email_generator, 20), 2)
  5. Install StreamData

    main

    Add stream_data to your project dependencies. It is recommended to include it only in the :test environment as it is primarily used for testing and test data generation.

    To also import StreamData's formatter configuration, add the :dev environment as well as :test for stream_data and add :stream_data to your .formatter.exs file.

    # In mix.exs
    defp deps() do
      [{:stream_data, "~> 1.0", only: :test}]
    end
    # In .formatter.exs
    [
      import_deps: [:stream_data]
    ]