DEV Community

Fernando Correa de Oliveira
Fernando Correa de Oliveira

Posted on

4 1

Testing Raku Applications with Cro, Red, and RedFactory

Introduction

Automated testing is crucial in modern software development. It helps ensure quality and prevent regressions by quickly verifying if the system continues working after code changes. Unlike manual tests, automated tests can be run repeatedly, ensuring consistency and speeding up validation. Moreover, they improve reliability by confirming that software inputs and outputs remain correct and aligned with requirements.

In this article, we’ll explore how to test applications written in Raku using three main tools: Cro, Red, and RedFactory.

  • Cro is a set of libraries for building reactive services (such as web applications and HTTP APIs) in Raku.
  • Red is an Object-Relational Mapper (ORM) for Raku. Red allows you to interact with relational databases using Raku objects instead of writing raw SQL, bringing several advantages like type safety, consistency, and easier schema evolution.
  • RedFactory is a helper library designed to work with Red, making it easy to generate consistent test data.

Combining Cro, Red, and RedFactory, we can create web applications in Raku backed by a database and write efficient automated tests for them. Let’s walk step-by-step through setting up a simple project with these tools, defining database models, using factories to generate test data, and writing automated tests—including full HTTP integration tests using Cro::HTTP::Test.

Setting Up the Environment

To set up a basic project using Cro, Red, and RedFactory:

  1. Install Raku and dependencies: Ensure you have Raku installed. Then install Cro, Red, and RedFactory using zef:
zef install cro Red RedFactory
Enter fullscreen mode Exit fullscreen mode
  1. Create a new Cro project: Cro provides a CLI tool to generate project skeletons:
cro stub http MyApp myapp
Enter fullscreen mode Exit fullscreen mode

This creates a project under myapp/ with initial files and structure.

  1. Add Red to the project: Import Red into your main script or modules:
use Red:api<2>;
Enter fullscreen mode Exit fullscreen mode

Configure the database connection:

red-defaults "SQLite", database => "myapp.db";
Enter fullscreen mode Exit fullscreen mode
  1. Install and configure RedFactory: You already installed RedFactory in step 1. Create a file like lib/Factories.rakumod where you will define factories for your models.

Now your environment is ready: a basic Cro project running, Red handling database interaction, and RedFactory set up to create test data easily.

Creating Models with Red

Models represent database tables as Raku classes. Let’s define a simple model for children, storing their name and country:

use Red:api<2>;

model Child {
    has UInt $.id       is serial;
    has Str  $.name     is column;
    has Str  $.country  is column;
}
Enter fullscreen mode Exit fullscreen mode
  • is serial marks the ID as an auto-increment primary key.
  • is column indicates the attribute maps to a database column.

Before using the model, ensure the table exists:

Child.^create-table: :unless-exists;
Enter fullscreen mode Exit fullscreen mode

Now you can create records:

Child.^create: name => "Alice", country => "Brazil";
Enter fullscreen mode Exit fullscreen mode

Or query them:

for Child.^all -> $child {
    say $child.name, " - ", $child.country;
}
Enter fullscreen mode Exit fullscreen mode

Red abstracts away SQL, making database operations much simpler and safer.

Creating Factories with RedFactory

Factories allow us to create consistent test data easily. Define them like this in Factories.rakumod:

use RedFactory;
use Child;

factory "child", :model(Child), {
    .name    = "Test";
    .country = "Unknown";
}
Enter fullscreen mode Exit fullscreen mode

Now you can create test records in one line:

my $child = factory-create "child";
Enter fullscreen mode Exit fullscreen mode

Or override attributes:

factory-create "child", :name<Alice>;
Enter fullscreen mode Exit fullscreen mode

You can even create multiple records:

factory-create 5, "child";
Enter fullscreen mode Exit fullscreen mode

Factories make tests cleaner and easier to maintain.

Introduction to Cro::HTTP::Test

Testing HTTP routes without running a real server is made easy by Cro::HTTP::Test.

This module allows you to:

  • Execute your routes in-memory.
  • Send simulated HTTP requests.
  • Check responses declaratively.

Example:

use Test;
use Cro::HTTP::Test;
use MyApp::Routes;

test-service routes(), {
    test get("/hello/World"),
        status       => 200,
        content-type => 'text/plain',
        body         => "Hello, World!";
};
done-testing;
Enter fullscreen mode Exit fullscreen mode

test-service wraps your routes for testing.
test sends a request and checks expectations like status code, content type, and body.

No need to bind to ports or manage real HTTP connections—everything is fast and isolated.

Writing Tests

Let’s use RedFactory and Cro::HTTP::Test together.

Imagine we have this API route:

get -> 'children', 'count', $country {
    my $count = Child.^all.grep(*.country eq $country).elems;
    content 'application/json', { :count($count) };
}
Enter fullscreen mode Exit fullscreen mode

We can test it:

use Test;
use Cro::HTTP::Test;
use Factories;
use MyApp::Routes;

factory-run {
    Child.^create-table: :unless-exists;

    .create: 10, "child", :country<UK>;

    test-service routes(), {
        test get("/children/count/UK"),
            status       => 200,
            content-type => 'application/json',
            json         => { :count(10) };
    };
}
done-testing;
Enter fullscreen mode Exit fullscreen mode
  • factory-run sets up a fresh SQLite in-memory database.
  • We create 10 children from the UK.
  • Then we test that the /children/count/UK route correctly returns 10.

This shows how Cro, Red, and RedFactory work together cleanly.

Full Integration Example

Suppose our API has:

  • GET /children — list all children.
  • POST /children — create a new child.

We can test the full flow:

use Test;
use Cro::HTTP::Test;
use Factories;
use MyApp::Routes;

factory-run {
    Child.^create-table: :unless-exists;

    test-service routes(), {
        test get("/children"),
            status => 200,
            content-type => 'application/json',
            json => [];
    };

    my %new-data = ( name => "John", country => "Portugal" );
    test-service routes(), {
        test post("/children", json => %new-data),
            status => 201,
            content-type => 'application/json',
            json => { :name("John"), :country("Portugal"), *%_ };
    };

    test-service routes(), {
        test get("/children"),
            status => 200,
            json => [ { :name("John"), :country("Portugal"), *%_ } ];
    };
}
done-testing;
Enter fullscreen mode Exit fullscreen mode

We:

  • Start with an empty list.
  • Add a child via POST.
  • Confirm the list now contains the new child.

Simple, clean, and fully automated.

Best Practices

  • Isolated Database for Tests: Always use a test database, ideally in-memory. factory-run with RedFactory makes this easy.
  • Use Factories: Define clear factories for your models to make tests readable.
  • Separate Unit and Integration Tests: Keep logic-only tests separate from HTTP integration tests.
  • Check Only Necessary Parts: Use partial JSON assertions with *%_ to avoid fragile tests.
  • Organize Code: Keep routes modular and tests structured.

Good practices help you keep tests maintainable, readable, and fast.

Conclusion

In this article, we showed how to test Raku applications using Cro, Red, and RedFactory:

  • Set up a project environment.
  • Define database models.
  • Create factories to generate test data.
  • Use Cro::HTTP::Test to simulate HTTP requests and verify responses.

The Red ORM helps you avoid boilerplate SQL and interact with the database elegantly.
RedFactory makes test data creation effortless.
Cro::HTTP::Test allows fast, isolated HTTP testing.

If you’re building web applications in Raku, combining these tools will dramatically boost your productivity and confidence in your code quality.

Give Red a try in your next Raku project—you’ll love how much easier database interactions and testing become!

Sentry image

Make it make sense

Only the context you need to fix your broken code with Sentry.

Start debugging →

Top comments (0)

Image of Datadog

Keep your GPUs in check

This cheatsheet shows how to use Datadog’s NVIDIA DCGM and Triton integrations to track GPU health, resource usage, and model performance—helping you optimize AI workloads and avoid hardware bottlenecks.

Get the Cheatsheet

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay