Using seed and changeset generators with child resources

I have a Child resource that belongs_to a Parent. I'd like to use the seed and changeset generators with these resources in my tests. I'd like to - generate the Child along with a unique Parent in one generator call - child = generate(child_seed()) - child = generate(child_changeset()) - optionally accept a parent id to bypass parent generation - child = generate(child_seed(parent_id: 123)) - child = generate(child_changeset(parent_id: 123)) - have this work in property tests My first try with the changeset generator looks like this. It works, but I don't like it.
def child_changeset(opts \\ []) do
changeset_generator(
Child,
:create,
uses: %{},
defaults: fn %{} ->
if Keyword.get(opts, :parent_id) do
[]
else
parent = generate(ParentGenerator.parent_changeset())
[parent_id: parent.id]
end
end,
overrides: opts
)
end
def child_changeset(opts \\ []) do
changeset_generator(
Child,
:create,
uses: %{},
defaults: fn %{} ->
if Keyword.get(opts, :parent_id) do
[]
else
parent = generate(ParentGenerator.parent_changeset())
[parent_id: parent.id]
end
end,
overrides: opts
)
end
I poked away at an implementation using seed_generator but failed to get something that worked. I have a few questions: - What is the best way to do this? - Does anyone else use the generators in this way? - Is the purpose of uses to wrap constants with StreamData.constant? - Why the difference in interface between seed_generator and changeset_generator beyond changeset_generator taking an action? Thanks
1 Reply
ZachDaniel
ZachDaniel5mo ago
uses is designed to compose other generators
def parent_changeset(opts \\ []) do

end

def child_changeset(opts \\ []) do
parent =
if opts[:parent] do
opts[:parent]
else
parent_changeset(opts[:parent])
end

changeset_generator(
Child,
:create,
uses: [parent: parent],
defaults: fn %{parent: parent} ->
[parent_id: parent.id]
end,
overrides: opts
)
end
def parent_changeset(opts \\ []) do

end

def child_changeset(opts \\ []) do
parent =
if opts[:parent] do
opts[:parent]
else
parent_changeset(opts[:parent])
end

changeset_generator(
Child,
:create,
uses: [parent: parent],
defaults: fn %{parent: parent} ->
[parent_id: parent.id]
end,
overrides: opts
)
end
So you can do things like that It depends a bit on your setup and what opts you want to be able to pass to your generator i.e parent vs parent_id etc. seed_generator is very low level, writing directly to the data layer, and changeset_generator is using your real domain actions, so their inputs are ultimately pretty different we suggest preferring changeset_generators, and falling back to seed generators where necessary Another thing you can do:
def parent_changeset(opts \\ []) do

end

def child_changeset(opts \\ []) do
parent_id =
opts[:parent_id] || generate(parent_changeset(...)).id

changeset_generator(
Child,
:create,
defaults: [parent_id: parent.id],
overrides: opts
)
end
def parent_changeset(opts \\ []) do

end

def child_changeset(opts \\ []) do
parent_id =
opts[:parent_id] || generate(parent_changeset(...)).id

changeset_generator(
Child,
:create,
defaults: [parent_id: parent.id],
overrides: opts
)
end
is just generate a parent when you call the child changeset generator

Did you find this page helpful?