Skip to content

Unit Testing Skill

Create unit tests for Ioto applications using the TestMe framework. Tests can be written in C, TypeScript, or Shell.

Invoke with: /unit-test

Running Tests

bash
make test                       # Full suite from project root
cd test && tm                   # Full suite via TestMe
cd test && tm r/string          # One test by name
cd test && tm "web/security-*"  # Pattern matching

Install TestMe

TestMe requires Bun. Install TestMe globally:

bash
bun install -g --trust @embedthis/testme

Initialize a Test Directory

bash
mkdir test && cd test
tm --init                       # Creates testme.json5
tm --new math.c                 # Creates math.tst.c from template
tm --new api.ts                 # Creates api.tst.ts from template
tm --new check.sh               # Creates check.tst.sh from template

Writing C Tests

Simple Test (Non-Fiber)

c
#include "testme.h"
#include "ioto.h"

static void testBasicOperation()
{
    char *buf = rAlloc(256);
    tnotnull(buf);
    scopy(buf, 256, "hello");
    tmatch(buf, "hello");
    rFree(buf);
}

int main(void)
{
    rInit(0, 0);
    testBasicOperation();
    rTerm();
    return 0;
}

Fiber-Based Test

c
#include "test.h"

static void testRequest()
{
    Url *up = urlAlloc(0);
    int status = urlFetch(up, "GET", sfmt("%s/index.html", HTTP), NULL, 0, NULL);
    teqi(status, 200);
    tcontains(urlGetResponse(up), "Hello");
    urlFree(up);
}

static void fiberMain(void *data)
{
    testRequest();
    rStop();
}

int main(void)
{
    rInit(fiberMain, 0);
    rServiceEvents();
    rTerm();
    return 0;
}

C Assertion API

c
/* Boolean */
ttrue(expr);
tfalse(expr);

/* Integer comparison */
teqi(actual, expected);         /* equal */
tneqi(actual, expected);        /* not equal */
tgti(actual, expected);         /* greater than */

/* String comparison */
tmatch(actual, expected);       /* strings match (null-tolerant) */
tcontains(str, substr);         /* string contains substring */

/* Pointer checks */
tnotnull(ptr);
tnull(ptr);

/* Control */
tfail(fmt, ...);                /* force failure with message */
tskip(reason);                  /* skip test with reason */
tinfo(fmt, ...);                /* trace output */

Writing TypeScript Tests

typescript
import {expect, describe, test} from 'testme'

await describe('Feature', async () => {
    test('basic operation', () => {
        expect(2 + 2).toBe(4)
        expect(result).toEqual({status: 'ok'})
        expect(list).toContain('item')
    })
})

Writing Shell Tests

bash
#!/usr/bin/env bash
if ! command -v ioto-http &>/dev/null; then
    echo "FAIL: ioto-http not found"
    exit 1
fi
echo "PASS: all checks passed"
exit 0

File Naming

  • C tests: feature.tst.c
  • TypeScript tests: feature.tst.ts
  • Shell tests: feature.tst.sh
  • The .tst marker in the filename is required for TestMe discovery.
  • Test names must be unique across the test suite.