# Unit testing

When writing code, it can be very convenient to be able to test individual subroutines in isolation. For instance, when writing a sorting routine, it is nice to be able to test that in a few different scenarios. This will give you extra confidence when refactoring the sorting algorithm, since if the tests still pass you know you didn't break anything.

# Defining a test

A test can be defined anywhere in your source files, and looks like a bit like this:

.test "test_name" {
    // Do some setup
    lda #123
    sta foo
    
    jsr my_subroutine
    
    // Check the output
    .assert cpu.x == 7
    .assert ram(foo) = 123
    
    // Exit the test
    brk
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Running tests

You can run tests by invoking the mos test command. Every test will be assembled individually and run on an emulated 6502.

Please note that if you have defined banks then the unit test will only have access to the bank it is defined in.

# Assertions

In every part of your test (and even the subroutines you are testing) you can add .assert directives. The expression provided should be true, otherwise the test fails.

# Available flags

Since the tests are run on an emulated 6502 you can access CPU and memory information in your assertions as you well:

Key Description
cpu.a The accumulator (A) register
cpu.x The X register
cpu.y The Y register
cpu.sp The stack pointer
* The program counter
cpu.flags.zero The Z flag
cpu.flags.carry The C flag
cpu.flags.interrupt_disable The I flag
cpu.flags.decimal The D flag
cpu.flags.overflow The V flag
cpu.flags.negative The N flag
ram(...) Read a byte from ram, e.g. ram($d020)
ram16(...) Read a word from ram, e.g. ram16($0314)

# Custom failure message

When an assertion fails, it will log the expression that was being tested. You can optionally also provide a custom message, like so:

.assert cpu.y == 123 "this is a custom message"
1

# Tracing

You can add .trace directives to simply log some information during the test run, e.g.:

.trace (cpu.x, cpu.y, ram($d020))
1

Tracing only appears for failing tests, so if your test succeeds the tracing will not pollute your output.

If you don't pass in any parameters you will get a detailed CPU dump, e.g.:

.trace
1

Will print something like: * = $2000, SP = $FD, flags = -----I--, A = $00, X = $00, Y = $00