{"id":18027186,"url":"https://github.com/fuelen/seed_factory","last_synced_at":"2025-07-24T06:05:10.723Z","repository":{"id":166558200,"uuid":"642063427","full_name":"fuelen/seed_factory","owner":"fuelen","description":"A toolkit for test data generation","archived":false,"fork":false,"pushed_at":"2024-05-28T09:52:00.000Z","size":108,"stargazers_count":35,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-29T09:51:18.379Z","etag":null,"topics":["data-generation","elixir","exunit","factories","factory","fixtures","schema","testing","testing-tools"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fuelen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-17T18:32:18.000Z","updated_at":"2024-11-27T18:58:53.000Z","dependencies_parsed_at":"2023-12-14T17:34:12.225Z","dependency_job_id":"c73910aa-ab97-4211-bdfb-b2314fc0edd4","html_url":"https://github.com/fuelen/seed_factory","commit_stats":null,"previous_names":["fuelen/seed_factory"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/fuelen/seed_factory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuelen%2Fseed_factory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuelen%2Fseed_factory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuelen%2Fseed_factory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuelen%2Fseed_factory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fuelen","download_url":"https://codeload.github.com/fuelen/seed_factory/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuelen%2Fseed_factory/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266801409,"owners_count":23986371,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-24T02:00:09.469Z","response_time":99,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-generation","elixir","exunit","factories","factory","fixtures","schema","testing","testing-tools"],"created_at":"2024-10-30T08:10:00.058Z","updated_at":"2025-07-24T06:05:10.686Z","avatar_url":"https://github.com/fuelen.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"logo.svg\" alt=\"seed_factory\" height=\"200px\"\u003e\u003c/p\u003e\n\n# SeedFactory\n\n[![CI](https://github.com/fuelen/seed_factory/actions/workflows/elixir.yml/badge.svg)](https://github.com/fuelen/seed_factory/actions/workflows/elixir.yml)\n[![Hex.pm](https://img.shields.io/hexpm/v/seed_factory.svg)](https://hex.pm/packages/seed_factory)\n[![Coverage Status](https://coveralls.io/repos/github/fuelen/seed_factory/badge.svg?branch=main)](https://coveralls.io/github/fuelen/seed_factory?branch=main)\n[![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/seed_factory)\n\nA toolkit for test data generation.\n\nThe main idea of `SeedFactory` is to generate data in tests according to your application business logic (read as context functions if you use [Phoenix Contexts](https://hexdocs.pm/phoenix/contexts.html)) whenever it is possible and avoid direct inserts to the database (as opposed to `ex_machina`).\nThis approach allows you to minimize testing of invalid states as you're not forced to keep complex database structure in your head in order to prepare test data.\nThe library is completely agnostic to the database toolkit.\n\nSee docs for details \u003chttps://hexdocs.pm/seed_factory\u003e.\n\n## Installation\n\nThe package can be installed by adding `seed_factory` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:seed_factory, \"~\u003e 0.6\"}\n  ]\nend\n```\n\n## Overview\n\nThis section provides a couple of examples of what the API of the library looks like. For more comprehensive explanations please refer to [docs](https://hexdocs.pm/seed_factory). **README is NOT the primary source of documentation.**\n\nTo use the library, define a schema with commands that describe the processes of your application. When a command is executed it modifies the context by producing/updating/deleting entities.\n\nThere is a concept of traits. Think about them as labels which are assigned to produced/updated entities when specific commands with specific arguments are executed.\n\n### Schema example\n\n```elixir\ndefmodule MyApp.SeedFactorySchema do\n  use SeedFactory.Schema\n\n  command :create_company do\n    param :name, generate: \u0026Faker.Company.name/0\n\n    resolve(fn args -\u003e\n      with {:ok, company} \u003c- MyApp.Companies.create_company(args) do\n        {:ok, %{company: company}}\n      end\n    end)\n\n    produce :company\n  end\n\n  command :create_user do\n    param :name, generate: \u0026Faker.Person.name/0\n    param :role, value: :normal\n    param :company, entity: :company\n\n    resolve(fn args -\u003e MyApp.Users.create_user(args.company, args.name, args.role) end)\n\n    produce :user\n    produce :profile\n  end\n\n  command :activate_user do\n    param :user, entity: :user, with_traits: [:pending]\n\n    resolve(fn args -\u003e\n      user = MyApp.Users.activate_user!(args.user)\n\n      {:ok, %{user: user}}\n    end)\n\n    update :user\n  end\n\n  trait :pending, :user do\n    exec :create_user\n  end\n\n  trait :active, :user do\n    from :pending\n    exec :activate_user\n  end\n\n  trait :admin, :user do\n    exec :create_user, args_pattern: %{role: :admin}\n  end\n\n  trait :normal, :user do\n    exec :create_user, args_pattern: %{role: :normal}\n  end\nend\n```\n\n### Usage example\n\n```elixir\nimport SeedFactory\n\ncontext = %{}\n# Put metadata about the schema to the context with the help of init/2 function\ncontext = init(context, MyApp.SeedFactorySchema)\n\n# Now, we can execute a command with automatically generated args using exec/2\n%{company: _} = exec(context, :create_company)\n\n# Arguments can be passed explicitly using exec/3\n%{company: _, user: _, profile: _} =\n  context\n  |\u003e exec(:create_company, name: \"GitHub\")\n  |\u003e exec(:create_user, name: \"John Doe\")\n  |\u003e exec(:activate_user)\n\n# Dependent entities are produced automatically if there is no such entity in the context.\n# In this example, :create_company will be executed implicitly, because :create_user depends on :company\n%{company: _, user: _} = exec(context, :create_user)\n\n# If you're fine with generated arguments, then you can use produce/2 to specify\n# desired entities and the chain of corresponding commands will be executed automatically\n%{company: _company} = produce(context, :company)\n%{user: _user, company: _company} = produce(context, [:company, :user])\n\n# Rebind entities to another names\n%{profile1: _, user1: _} = produce(context, user: :user1, profile: :profile1)\n\n# Specify traits\n%{user: _user} = produce(context, user: [:admin, :active])\n\n# The command above is an alternative to\n%{user: _user} =\n  context\n  |\u003e exec(:create_user, role: :admin)\n  |\u003e exec(:activate_user)\n```\n\nUsage with `ExUnit`:\n```elixir\ndefmodule MyApp.MyTest do\n  use ExUnit.Case\n  use SeedFactory.Test, schema: MyApp.SeedFactorySchema\n\n  describe \"produce/1 macro\" do\n    produce :company, user: [:active, :admin, as: :active_admin]\n\n    test \"inspect data\", %{company: company, active_admin: active_admin} do\n      dbg(company)\n      dbg(active_admin)\n    end\n  end\n\n\n  describe \"produce/2 and exec/3 functions\" do\n    test \"demo #1\", ctx do\n      ctx =\n        ctx\n        |\u003e exec(:create_company, name: \"GitHub\")\n        |\u003e produce(user: [:normal, :active])\n\n      dbg(ctx)\n    end\n\n    test \"demo #2\", ctx do\n      ctx\n      |\u003e produce(company: :company1, user: :user1, profile: :profile1)\n      |\u003e produce(company: :company2, user: :user2, profile: :profile2)\n      |\u003e dbg()\n    end\n\n    test \"demo #3\", ctx do\n      ctx = produce(ctx, :company)\n      ctx1 = ctx |\u003e exec(:create_user, name: \"John\")\n      ctx2 = ctx |\u003e exec(:create_user, name: \"Jane\") |\u003e exec(:activate_user)\n\n      dbg(ctx1)\n      dbg(ctx2)\n    end\n  end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuelen%2Fseed_factory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffuelen%2Fseed_factory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuelen%2Fseed_factory/lists"}