Insert Fake data in the test db

I am writing test cases for my application . I am trying to write generic test cases . I am using faker to insert the fake data. I want to insert the data inside the map depending upon the data type
I am converting schema struct to map first like this:

   Map.from_struct(struct(Model))

I can access the data types with schema functions:

   Model.___schema__(:type, field)

It return the data type of the fields.

I can use comprehension to return the data types of all the fields:

  num = [schema_fields]

 for x <- num, do: @model.__schema__(:type, x)

I cannot use if statement because it wont allow invoke functions inside of it.

So any suggestions how can insert data inside the map depending upon the data type?

Thanks!

You can apply filter with for comprehension…

From https://hexdocs.pm/elixir/Kernel.SpecialForms.html#for/1

# A comprehension with a generator and a filter
iex> for n <- [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n
[2, 4, 6]

# with a guard clause
iex> for {type, name} when type != :guest <- users, do: String.upcase(name)

Or look at http://elixir-recipes.github.io/lists/list-comprehension/

What exactly are you trying to do? Something like fixtures? I usually define a module in test/support for it and it looks like

defmodule MyApp.Fixtures do
  alias MyApp.{Repo, Post}

  @spec fixture(atom, Keyword.t()) :: struct | no_return
  def fixture(resource_name, attrs \\ [])

  def fixture(:post, attrs) do
    Repo.insert!(%Post{
      title: attrs[:title] || Faker.Lorem.sentence(2),
      body: attrs[:body] || Faker.Lorem.sentence(10),
      location: attrs[:location],
      inserted_at: attrs[:inserted_at],
      updated_at: attrs[:updated_at]
    })
  end
 # ... more fixtures ...
end

then I import it in *_case.ex files and use it in tests like fixture(:post) or with custom attributes fixture(:post, title: "custom title").

1 Like

build a map dynamically and then insert it into the changeset inside a test and validate the changeset

So something like a quickcheck?

some thing that will check the type of the field and then insert the fake data. like if its s timestamp it insert faker.Date.backwards(3) and if its string then faker.Name.namelike this. Build a map dynamically for every model.

But do you actually test the changeset with this if you supply the data it already expects and works with? Why doesn’t static data doesn’t work for you? Changesets are supposed to be pure anyway.

it will duplicate the code a lot. I f i write this for every model. So I am thinking about writing a macro that will be included in every model test. thats why I want to write it generically

Can you do it with a function maybe?

Macros are better they will be added automatically at the compile time and and they can inherit all the functionality of your current module unlike functions. I already wrote a marcro its working fine. Now i only need to make it generic

With a function I would do it like this

defmodule Test do
  @spec attrs_for_schema(module) :: %{atom => term}
  def attrs_for_schema(schema) do  
    schema.__schema__(:fields)
    |> Stream.map(fn field -> {field, schema.__schema__(:type, field)} end)
    |> Enum.reduce(%{}, fn {field, type}, acc ->
      Map.put(acc, field, random_value_for_type(type))
    end)
  end

  defp random_value_for_type(:id), do: :rand.uniform(1000)
  defp random_value_for_type(:date), do: Faker.Date.backwards(3)
  defp random_value_for_type(:string), do: Faker.Name.name()
  defp random_value_for_type(_other), do: nil # or raise
end
iex(2)> Test.attrs_for_schema(MyApp.Post)
%{
  body: "Drake Quitzon",
  id: 361,
  parent_id: 482,
  place_id: 752,
  title: "Chet Abshire",
  user_id: 298,
  location: nil
}
1 Like

Thanks I can use this logic to implement it.