← Code Compare

Testing

A minimal unit test for a function. We define a tiny add function and check it against a couple of expected results. Most languages ship a test idiom in the standard library or runtime; where none exists, an assertion that aborts on failure plays the same role.

Show: GujiGoOCamlHaskellPerlRakuRustPython
Guji
sub add($a: Int, $b: Int): Int {
    $a + $b
}

sub assert_eq($actual: Int, $expected: Int, $name: Str): Unit {
    if $actual == $expected {
        print("ok - $name")
    } else {
        panic("FAIL $name: expected $expected, got $actual")
    }
}

sub main() {
    assert_eq(add(2, 3), 5, "adds positives")
    assert_eq(add(-1, 1), 0, "adds to zero")
}

guji (v0.1-alpha) has no dedicated test framework yet, so an idiomatic check is an assert_eq helper built from the prelude's print and the panic intrinsic (§11.2, §15.4). A passing case prints a TAP-style ok line; a mismatch calls panic, which writes the message to stderr and aborts with a non-zero exit code. The whole file runs as an ordinary main, so the same program is both the test and its runner - and since guji compiles, you can run it with the interpreter (guji file.guji) or build a native binary (guji build -o test file.guji), which behave identically.

Go
package add

func Add(a, b int) int {
	return a + b
}

// add_test.go
package add

import "testing"

func TestAdd(t *testing.T) {
	cases := []struct {
		name string
		a, b int
		want int
	}{
		{"adds positives", 2, 3, 5},
		{"adds to zero", -1, 1, 0},
	}
	for _, c := range cases {
		if got := Add(c.a, c.b); got != c.want {
			t.Errorf("%s: Add(%d, %d) = %d, want %d", c.name, c.a, c.b, got, c.want)
		}
	}
}

Go's testing package is part of the standard toolchain: any TestXxx(t *testing.T) function in a _test.go file is discovered and run by go test. The idiomatic style is table-driven, iterating over a slice of cases and calling t.Errorf to record a failure without halting the rest of the loop. No assertion library is needed, only the t handle.

OCaml
let add a b = a + b

let check name actual expected =
  if actual = expected then
    Printf.printf "ok - %s\n" name
  else
    failwith (Printf.sprintf "FAIL %s: expected %d, got %d" name expected actual)

let () =
  check "adds positives" (add 2 3) 5;
  check "adds to zero" (add (-1) 1) 0

Real OCaml projects reach for a framework such as Alcotest or ppx_inline_test, but the language core already gives a self-contained check: structural equality = plus failwith, which raises Failure to abort with a message on a mismatch. Each check prints an ok line when the assertion holds. Running the executable is the test run, exiting non-zero on the first failure.

Haskell
module Main (main) where

import Control.Monad (unless)

add :: Int -> Int -> Int
add a b = a + b

check :: String -> Int -> Int -> IO ()
check name actual expected =
  unless (actual == expected) $
    error ("FAIL " ++ name ++ ": expected " ++ show expected ++ ", got " ++ show actual)

main :: IO ()
main = do
  check "adds positives" (add 2 3) 5
  check "adds to zero" (add (-1) 1) 0
  putStrLn "all tests passed"

Production Haskell uses HUnit, tasty, or property testing with QuickCheck, but the pure core plus base is enough for a minimal check. check compares the result with == and, on a mismatch, calls error, which throws and aborts with a non-zero exit. The pure add is tested by an IO driver in main; reaching putStrLn means every assertion held.

Perl
use strict;
use warnings;
use Test::More tests => 2;

sub add { my ($a, $b) = @_; return $a + $b; }

is(add(2, 3), 5, 'adds positives');
is(add(-1, 1), 0, 'adds to zero');

Test::More is the standard Perl testing module and emits TAP (Test Anything Protocol), the output format harnesses like prove understand. is compares actual against expected and prints ok/not ok with the description, while the tests => 2 plan guards against the script dying early and silently skipping checks. The file is run directly with perl, or under prove for a summary.

Raku
use Test;

sub add($a, $b) { $a + $b }

plan 2;
is add(2, 3), 5, 'adds positives';
is add(-1, 1), 0, 'adds to zero';

Raku ships the Test module in core, so no install is needed, and like Perl it emits TAP. plan 2 declares how many assertions to expect, and is checks equality and reports each result with its label. The file runs directly with raku, or under prove6/zef test inside a distribution.

Rust
fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adds_positives() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn adds_to_zero() {
        assert_eq!(add(-1, 1), 0);
    }
}

Rust has a built-in test harness: functions marked #[test] are compiled and run by cargo test. They live in a #[cfg(test)] module so the test code is excluded from normal builds, and use super::* pulls the parent module's items into scope. The assert_eq! macro panics with a helpful diff on a mismatch, which the harness records as a failed test.

Python
import unittest


def add(a, b):
    return a + b


class TestAdd(unittest.TestCase):
    def test_adds_positives(self):
        self.assertEqual(add(2, 3), 5)

    def test_adds_to_zero(self):
        self.assertEqual(add(-1, 1), 0)


if __name__ == "__main__":
    unittest.main()

unittest is in the standard library: test methods named test_* on a TestCase subclass are auto-discovered and run. assertEqual reports a clear message when the values differ, and unittest.main() lets the file run directly as a test suite. Many projects prefer pytest for terser plain-assert tests, but unittest needs no dependencies.