PropSchema v0.3.0 PropSchema

An extension on Ecto.Schema used to generate property tests. Schema can be further augmented based on additional options, which will be read by the corresponding PropSchema.Executor module to generate tests.

Example

  prop_schema "example" do
    prop_field(:example_string, :string, string_type: :alphanumeric, required: true)
    prop_field(:example_int, :integer, postive: true, required: false)
    field(:example_float, :float)
  end

This declares the schema "example" with three fields: :example_string, :example_int, and :example_float. Property test will be generated for :example_string and :example_int but, since :example is declared using the regular Ecto.Schema.field/3, a test will not be generated for it.

Adding these are equivalent to writing these tests:

  property("valid changeset") do
    check(all(map <- StreamData.fixed_map([{"example_int", StreamData.one_of([StreamData.integer(), StreamData.constant(nil)])}, {"example_string", StreamData.string(:alphanumeric, min_length: 1)}]))) do
      changeset = PropSchema.ExampleModule.changeset(struct(PropSchema.ExampleModule), map)
      (fn changeset ->
        if(not(changeset.valid?())) do
          Logger.error("Test will fail because: #{inspect(changeset.errors())}")
        end
        assert(changeset.valid?())
      end).(changeset)
    end

  property("valid changeset - missing example_int") do
    check(all(map <- StreamData.fixed_map([{"example_string", StreamData.string(:alphanumeric, min_length: 1)}]))) do
      changeset = PropSchema.ExampleModule.changeset(struct(PropSchema.ExampleModule), map)
      (fn changeset ->
        if(not(changeset.valid?())) do
          Logger.error("Test will fail because: #{inspect(changeset.errors())}")
        end
        assert(changeset.valid?())
      end).(changeset)
    end
  end

  property("invalid changeset - missing example_string") do
    check(all(map <- StreamData.fixed_map([{"example_int", StreamData.one_of([StreamData.integer(), StreamData.constant(nil)])}]))) do
      changeset = PropSchema.ExampleModule.changeset(struct(PropSchema.ExampleModule), map)
      (fn changeset ->
        if(changeset.valid?()) do
          Logger.error("Test will fail because: No errors")
        end
        refute(changeset.valid?())
      end).(changeset)
    end
  end

I you would like to see what tests are being generated for your schema, use the mix task:

  $ mix prop_schema.print <module> [options]

Docs can be found here: Mix.Tasks.PropSchema.Print

Or for a more realistic example see example: module, properties, and generated tests.

Link to this section Summary

Functions

Declares a field in __prop_schema__/2 which will either be the name with "_id" appended or the value of the :foreign_key option. A UUID generator is provided for uid ids

Declares a field in the schema, processes it for use in PropSchema.Executor and then passes it through to Ecto.Schema.field/3. See prop_schema/2 for examples

Declares a field in __prop_schema__/2 which corresponds with a provided generator that will build the list of associated structs for you. The only addition to the normal Ecto.Schema.has_many/3 call is the :additional_props option which will tell the generator where to find the additional props for building the associated structs

Declares a field in __prop_schema__/2 which corresponds with a provided generator that will build the associated struct for you. The only addition to the normal Ecto.Schema.has_one/3 call is the :additional_props option which will tell the generator where to find the additional props for building the associated struct

Declares a field in __prop_schema__/2 which corresponds with a provided generator that will build the list of associated structs for you. The only addition to the normal Ecto.Schema.many_to_many/3 call is the :additional_props option which will tell the generator where to find the additional props for building the associated structs. The associated struct’s __prop_schema__/2 results will be modified so as to not generate it’s associated many_to_many structs due to the endless recursion that would cause

Declares an Ecto.Schema with additional options that are used to generate property tests using PropSchema.Executor

Link to this section Functions

Link to this macro prop_belongs_to(name, queryable, opts \\ []) (macro)
prop_belongs_to(atom(), module(), Keyword.t()) ::
  PropSchema.Types.ast_expression()

Declares a field in __prop_schema__/2 which will either be the name with "_id" appended or the value of the :foreign_key option. A UUID generator is provided for uid ids.

Link to this macro prop_embedded(list) (macro)

High school english class time! prop_embedded/1 is to prop_schema/2 as Ecto.Schema.embedded_schema/1 is to Ecto.Schema.schema/2

Examples

prop_embedded do
  prop_field(:example_string, :string, string_type: :alphanumeric, required: true)
  prop_field(:example_int, :integer, postive: true, required: false)
  field(:example_float, :float)
end
Link to this macro prop_field(name, type \\ :string, opts \\ []) (macro)

Declares a field in the schema, processes it for use in PropSchema.Executor and then passes it through to Ecto.Schema.field/3. See prop_schema/2 for examples.

Link to this macro prop_has_many(name, queryable, opts \\ []) (macro)

Declares a field in __prop_schema__/2 which corresponds with a provided generator that will build the list of associated structs for you. The only addition to the normal Ecto.Schema.has_many/3 call is the :additional_props option which will tell the generator where to find the additional props for building the associated structs.

Link to this macro prop_has_one(name, queryable, opts \\ []) (macro)

Declares a field in __prop_schema__/2 which corresponds with a provided generator that will build the associated struct for you. The only addition to the normal Ecto.Schema.has_one/3 call is the :additional_props option which will tell the generator where to find the additional props for building the associated struct.

Link to this macro prop_many_to_many(name, queryable, opts \\ []) (macro)
prop_many_to_many(atom(), module(), Keyword.t()) ::
  PropSchema.Types.ast_expression()

Declares a field in __prop_schema__/2 which corresponds with a provided generator that will build the list of associated structs for you. The only addition to the normal Ecto.Schema.many_to_many/3 call is the :additional_props option which will tell the generator where to find the additional props for building the associated structs. The associated struct’s __prop_schema__/2 results will be modified so as to not generate it’s associated many_to_many structs due to the endless recursion that would cause.

Link to this macro prop_schema(source, list) (macro)

Declares an Ecto.Schema with additional options that are used to generate property tests using PropSchema.Executor.

Field Declaration

When a field is declared with prop_field/3 it will add the extra values as conditions for generating property tests. It is possible to declare a field just by using Ecto.Schema.field/3 however PropSchema.Executor will not know about it, and tests will not be generated for that field.

Examples

prop_schema "example" do
  prop_field(:example_string, :string, string_type: :alphanumeric, required: true)
  prop_field(:example_int, :integer, postive: true, required: false)
  field(:example_float, :float)
end