Day 12: RedFactory

Since the elves started using Red (https://raku-advent.blog/2019/12/21/searching-for-a-red-gift/) they thought it was missing a better way of testing code that uses it. They tested it using several SQL files that would be used before each test to populate the database with test data. That works ok, but that’s too hard to understand what’s expected from the test not looking at those SQL files. It also added a big chunk of boilerplate at the beginning of each test file for runnig the SQL. In every file it’s the same code, changing only what file to use. So they decided to look for some better way of doing that.

Searching for it they found a new module called RedFactory. It’s specific for Red and uses factories to make it easier to write and read tests written for code that uses Red. The idea about factories is to have a easy way of adding data to your test DB with default values making that easy to populate the test DB at the same file as the test and setting speccific values only for what is needed on the test.

The first thing to be done to use factories is to create the factories themselves. so, for testing the code created here first we would need to create a factory like this one:

use Child;
use Gift;
factory "child", :model(Child), {
.name = "Aline";
.country = "Brazil";
}
factory "gift", :model(Gift), {
.name = "a gift";
}

That creates 2 factories, one for Child model and another one for Gift model called child and gift respectively. A factory doesn’t need to have the same name as its model, but the first factory for a model usualy does. Other factories for that model usualy get more specific names and has more specialised data;

child factory sets default values for 2 columns (name and country) while gift sets only one (name). So lets see how to use that.

RedFactory‘s factories will use any Red‘s DB connection you set, so, if you do:

use Factories; # your factories module
my $*RED-DB = database "Pg", :host<some_host>;
my $child = factory-create "child";
view raw test.raku hosted with ❤ by GitHub

That will create a new Child entry on your Pg database. That row will contain:

idnamecountry
??AlineBrazil
(id will be the next value in the sequence)

And $child will have the object created by Red.

But you usualy don’t want to mess with your DB while testing. For helping with that, RedFactory has a helper for running that on a thrown-away DB. So, you could do that instead:

use Factories; # your factories module
my $*RED-DB = factory-db;
my $child = factory-create "child";
view raw test.raku hosted with ❤ by GitHub

That will work exactly as the other snippet, but using a SQLite database in memory. Another way of doing that is using factory-run that will receive a block that will use the in memoty SQLite DB and will receive the RedFactory object, where you can call its methods instead of using the factories functions, for example:

use Factories; # your factories module
factory-run {
my $child = .create: "child";
}
view raw test.raku hosted with ❤ by GitHub

And it will do exactly the same as the previous snippet.

Ok, that’s cool. But what about testing? Let’s do that! The elves’ code has a function to return the the number of children from a specific country (&children-on-country), so they started writing the test like this:

use Test;
use Factories; # your factories module
use Child::Helper; # imports &children-on-country
factory-run {
is children-on-country("UK"), 0;
.create: "child", :country<UK>;
is children-on-country("UK"), 1;
.create: 9, "child", :country<UK>;
is children-on-country("UK"), 10;
}
view raw test.raku hosted with ❤ by GitHub

This is using .create on child factory passing a country value to be used different from default. it also uses .createpassing an UInt as first param, that indicates .create to create as many rows as the number passed, and returns a list of those created objects.

But there is a “problem” with that. All created children will have the same name. We can do a small change on that factory to prevent that.

my @children-names = <Fernanda Sophia Eduardo Rafael Maria Lulu>;
my @countries = <Brazil England Scotland>;
factory "child", :model(Child), {
.name = { "{ @children-names.pick } { .counter-by-model }" };
.country = { @countries.pick };
}
view raw test.raku hosted with ❤ by GitHub

You can pass a block to to have it generate dynamic data for the column. That block will receive a Factory objects that over other things has a method that will return an incremented UInt every time it’s called (by model) (.counter-by-model).country is also changed, now it will return a random country for each raw created.

So, that made the elves’ tests much simpler to grok.

For more information about RedFactory, please look at https://github.com/FCO/RedFactory.

One thought on “Day 12: RedFactory

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: