84 lines
No EOL
3.4 KiB
Markdown
84 lines
No EOL
3.4 KiB
Markdown
---
|
|
date: 2023-10-25
|
|
title: creating predictable seed data
|
|
---
|
|
A challenge appears when there's a requirement to create a _lot_ of unique seed data. Traditionally the developer community would reach for randomized libraries such as [Chance](https://chancejs.com/index.html) while generating data such as a new user with a random email, name, gender etc. This can lead to hard to automation test code as you cannot guarantee what a user's data will look like other than it's generic shape. One common solution is to write static [test fixtures](https://en.wikipedia.org/wiki/Test_fixture#Software) which come with their own problems such as difficulty scaling, updating, and interdependencies.
|
|
|
|

|
|
|
|
Instead, what if we tied randomness to a static value such as an index within a loop (this can also be done with [static seeds](https://chancejs.com/usage/seed.html)). In the following examples we'll utilize a simple loop to create various types of predictable data.
|
|
|
|
```elixir
|
|
@def create_transaction do
|
|
count_to_generate = 150
|
|
amount_multiplier = 100
|
|
|
|
Enum.map(1..count_to_generate, fn index ->
|
|
# Example creating a money transaction
|
|
App.Transactions.create_transaction(%{
|
|
amount: &1 * amount_multiplier,
|
|
} end)
|
|
)
|
|
end
|
|
|
|
# Creates transactions with amounts tied to the index modified by some amount
|
|
# Ex: [%{amount: 100}, %{amount: 200}... %{amount: 15,000}]
|
|
```
|
|
|
|
But sure, you're saying this may only work for numbers. Well we can get clever and leverage looping over lists as well with this helper function...
|
|
|
|
```elixir
|
|
@doc """
|
|
Allows for being given a list, then passing the current index
|
|
to loop over the results like A, B, C, A, B, C...
|
|
## Examples
|
|
iex> repeating_loop_through_list(["A", "B", "C"], 1)
|
|
"B"
|
|
iex> repeating_loop_through_list(["A", "B", "C"], 3)
|
|
"A"
|
|
"""
|
|
@spec repeating_loop_through_list(list(), integer()) :: any()
|
|
def repeating_loop_through_list(list, index),
|
|
do: Enum.at(list, Integer.mod(index, Enum.count(list)))
|
|
```
|
|
|
|
With the above function we now can tie indexes to a list of repeating options.
|
|
|
|
```elixir
|
|
currencies = ["USD", "RUB", "JPY"]
|
|
Enum.map(1..count_to_generate, fn index ->
|
|
App.Transactions.create_transaction(%{
|
|
currency: repeating_loop_through_list(currencies, index),
|
|
})
|
|
end)
|
|
# Ex: [%{currency: "USD"}, %{currency: "RUB"}... %{currency: "USD"}]
|
|
```
|
|
|
|
Another fun trick is to leverage string padding to create example ID's or "codes" that may be tied to each item you're generating. I find this particularly useful while testing sorting.
|
|
|
|
```elixir
|
|
code_length = 3
|
|
padding_character = "0"
|
|
Enum.map(1..count_to_generate, fn index ->
|
|
App.Transactions.create_transaction(%{
|
|
code: String.pad_leading(to_string(index), code_length, padding_character),
|
|
})
|
|
end)
|
|
# Ex: [%{code: "001"}, %{code: "002"}... %{code: "150"}]
|
|
```
|
|
|
|
This works for dates as well!
|
|
|
|
```elixir
|
|
date_year = 2023
|
|
date_month = 01
|
|
max_date_day = 30
|
|
Enum.map(1..count_to_generate, fn index ->
|
|
App.Transactions.create_transaction(%{
|
|
sent_date: Date.new!(date_year, date_month, min(index, max_date_day)),
|
|
})
|
|
end)
|
|
# Ex: [%{date: ~D[2023-01-01]}, %{date: ~D[2023-01-02]}]
|
|
```
|
|
|
|
At the end of the day, this is just example test data. Good is better than perfect and so long as you're shipping something that looks and feels like the final product you're doing yourself, other developers, and QA a service. |