Skip to content

Custom Formatters

As of now (1/2026), tested currently supports a "terminal" output and a "plain" output (which is just the terminal output without colors), with plans for a few more in the future! But we've tried to make it easy to create your own formatter for tested.

A basic formatter

local custom_formatter = {
    -- whether or not this formatter allows filtering by type
    allow_filtering: boolean = true,

    -- name of formatter
    format: string = "my custom formatter"
}

-- Runs after performing all the setups and tests are about to run!
-- version: "tested v0.0.0"
-- filepaths: list of filepaths passed into tested.
function custom_formatter.header(version: string, filepaths: {string}) end

-- Displays results after a test has been run
function custom_formatter.results(
    tested_result: types.TestedOutput,
    test_types_to_display: {types.TestResult: boolean}
) 
end

-- 
function custom_formatter.summary(output: types.TestRunnerOutput) end

return custom_formatter

Feel free to also look at the existing terminal formatter and the references below to help you get started!

TestRunnerOutput

An example of what types.TestRunnerOutput looks like. types.TestedOutput is one of the values from module_results:

{
  all_fully_tested = false,
  module_results = { {
      counts = {
        failed = 0,
        invalid = 0,
        passed = 2,
        skipped = 0
      },
      filename = "./tests/fully_working_test.lua",
      fully_tested = true,
      tests = { {
          assertion_results = { {
              filename = "./tests/fully_working_test.lua",
              given = "nothing",
              line_number = 4,
              result = "PASS",
              should = "just work!"
            } },
          message = "All assertions have passed",
          name = "just works!",
          result = "PASS",
          time = 8.000000000008e-06
        }, {
          assertion_results = { {
              filename = "./tests/fully_working_test.lua",
              line_number = 13,
              result = "PASS"
            } },
          message = "All assertions have passed",
          name = "just works without given and should!",
          result = "PASS",
          time = 2.0000000000575e-06
        } },
      total_time = 1.0000000000066e-05
    }, {
      counts = {
        failed = 2,
        invalid = 0,
        passed = 1,
        skipped = 0
      },
      filename = "./tests/non_working_test.tl",
      fully_tested = false,
      tests = { {
          assertion_results = { {
              filename = "./tests/non_working_test.tl",
              given = "true",
              line_number = 4,
              result = "PASS",
              should = "1"
            } },
          message = "All assertions have passed",
          name = "doesn't work with given and should being not strings!",
          result = "PASS",
          time = 6.9999999999792e-06
        }, {
          assertion_results = { {
              error_message = "Actual: true\nExpected: false",
              filename = "./tests/non_working_test.tl",
              given = "true",
              line_number = 14,
              result = "FAIL"
            } },
          message = "1 assertions have failed",
          name = "output should clearly show just given",
          result = "FAIL",
          time = 2.9999999999752e-06
        }, {
          assertion_results = { {
              error_message = "Actual: false\nExpected: true",
              filename = "./tests/non_working_test.tl",
              line_number = 22,
              result = "FAIL",
              should = "true"
            } },
          message = "1 assertions have failed",
          name = "output should clearly show just should",
          result = "FAIL",
          time = 1.9999999999465e-06
        } },
      total_time = 1.1999999999901e-05
    } },
  total_counts = {
    failed = 2,
    invalid = 0,
    passed = 3,
    skipped = 0
  },
  total_tests = 5,
  total_time = 2.1999999999966e-05
}

Type Definitions

enum TestResult
  "PASS"
  "FAIL"
  "SKIP"
  "CONDITIONAL_SKIP"
  "EXCEPTION"
  "TIMEOUT"
  "UNKNOWN"
end

interface AssertionResult
  filename: string
  line_number: integer
  result: types.TestResult
  given: string
  should: string
  error_message: string
end

interface TestOutput
  name: string
  result: types.TestResult
  message: string
  time: number
  assertion_results: {AssertionResult}
end

interface TestCounts
  passed: integer
  failed: integer
  skipped: integer
  invalid: integer
end

interface TestedOutput
  tests: {TestOutput}
  filename: string
  counts: TestCounts
  fully_tested: boolean
  total_time: number
end

interface TestRunnerOutput
  module_results: {TestedOutput}
  total_counts: TestCounts
  total_time: number
  total_tests: integer
  all_fully_tested: boolean
end

-- The actual formatter itself
interface ResultFormatter
  format: string
  allow_filtering: boolean
  header: function(version: string, filepaths: {string})
  results: function(tested_result: types.TestedOutput, test_types_to_display: {types.TestResult: boolean})
  summary: function(runner_output: types.TestRunnerOutput)
end