Prototyping and enforcing context function conventions

The original post was regarding generating functions in the context. I would still like some tests in place, which tend to be even more repetitive and time-consuming than the context functions. Thus, a corresponding CrudTest module has been added. The idea here is, to the extent possible, match the API of the context function macros. The basic flow for each is to insert a factory/factories for the given resource, call the corresponding function, and then assert the results. As in the case of the context functions, if I need to add, change, or test these functions in ANY other way, I should just write out the tests and not use the macro. All of these macros generate a describe block with the function name and then include any number of test cases that make sense for the context function.

For example, CrudTest.list(@resource) will insert a few unit records and then assert that list_units() returns those records. Factory records are also created for various associations as needed in get_for!, preload, join, filter_by_*, etc. For most data types, order_by will generate data and assert the correct ordering.

The main difference is that the context function macros create, update, and change take a changeset as the second argument, whereas the test macros take actual test data (as a map or function) as that argument.

Most of the tests cover what I usually need for each case. The exception is in create and update. Those are purely smoke tests that ensure that passing valid data can create and update a record. In most cases, I will want to write out tests to cover error cases and such.

Factory names are based off the resource/association schema name. In cases where that does not match for the resource a factory option can be passed to config (ex. factory: :user). In case where associations do not match, an association_factories option can be passed to config (ex. [creator: :user, property: :building]).

Here is an example of tests for the corresponding functions in the original post above.

defmodule MyApp.Inventory.UnitTest do
  use MyApp.DataCase

  alias MyApp.Inventory.Unit

  @resource CrudTest.config(Unit, Repo)

  # Common CRUD function tests
  CrudTest.list(@resource)
  CrudTest.get!(@resource)
  CrudTest.get(@resource)
  CrudTest.new(@resource)
  CrudTest.create(@resource, %{name: "My Place"})
  CrudTest.change(@resource, %{name: "My Place"})
  CrudTest.update(@resource, %{name: "My Place"})
  CrudTest.delete(@resource)

  # CRUD helper tests
  CrudTest.paginate(@resource)
  CrudTest.get_for!(@resource, :network)
  CrudTest.get_for(@resource, :network)
  CrudTest.get_by_attr!(@resource, :name)
  CrudTest.get_by_attr(@resource, :name)
  CrudTest.preload(@resource, :property)
  CrudTest.join(@resource, :property)
  CrudTest.filter_by_one(@resource, :property)
  CrudTest.filter_by_one_or_many(@resource, :unit_type)
  CrudTest.order_by(@resource, :name, :desc)
end