Skip to content

YS Testing

YS is a great language for writing tests in.

Why?

Well tests are mostly data. Inputs and outputs for some code you want to test. Code is usually in functions, and functions usually have names and names are just strings and strings are just data.

YAML is a great format for data. YS is YAML with code (expressed as YAML data).

So it seems like YS should be a great language for writing tests in. And it is.

Inline Tests in YS🔗

Let's say we have a YS program called rot13.ys, and we want to have some tests for it inside the program.

We can do that like this:

#!/usr/bin/env ys-0

tests =::
- in: Hello, world!
  out: Uryyb, jbeyq!
- in: Uryyb, jbeyq!
  out: Hello, world!
- in: I like pie
  out: V yvxr cvr

defn run-tests():
  each test tests:
    got =: test.in:rot-13
    if got == test.out:
      say: "PASS - rot-13('$(test.in)') -> '$got'"
      say: "FAIL - '$got' != '$(test.out)'"

defn main(input=nil):
  if input:
    say: input:rot-13
    run-tests:

dict =: set(\\A .. \\Z) + (\\a .. \\z)
dict =: dict:cycle.drop(13 * 2).zipmap(dict)

defn rot-13(str):
  escape str: dict

We can run the program like this:

$ ys rot-13.ys 'rot-13 is cool'
ebg-13 vf pbby
$ ys rot-13.ys 
PASS - rot-13('Hello, world!') -> 'Uryyb, jbeyq!'
PASS - rot-13('Uryyb, jbeyq!') -> 'Hello, world!'
PASS - rot-13('I like pie') -> 'V yvxr cvr'
$

If we give it a string, it will print the ROT-13 of that string.

If we give it no arguments, it will run the tests.

The tests are just data and YAML is great for that.

The ys::taptest Module🔗

Writing inline tests can be useful once in a while, but it's not the best way to write tests for a real project.

Let's morph that example into something more like a real project.

Program file rot-13:

#!/usr/bin/env ys-0

use rot-13: :all

defn main(input=nil):
  say: input:rot-13

Library file rot-13.ys:

!YS-v0

ns: rot-13

dict =: set(\\A .. \\Z) + (\\a .. \\z)
dict =: dict:cycle.drop(13 * 2).zipmap(dict)

defn rot-13(str):
  escape str: dict

Test file rot-13.t:

#!/usr/bin/env ys-0

use rot-13: :all
use ys::taptest: :all

test::
- code: rot-13('Hello, world!')
  want: Uryyb, jbeyq!
- code: rot-13('Uryyb, jbeyq!')
  want: Hello, world!
- code: rot-13('I like pie')
  want: V yvxr cvr

This code is much cleaner. We don't have to write testing logic because the YS builtin ys::taptest module takes care of that.

Let's run the program and the tests:

$ ./rot-13 'Hello, world.'
Uryyb, jbeyq.
$ prove -v rot-13.t
rot-13.t ..
ok 1 - rot-13('Hello, world!')
ok 2 - rot-13('Uryyb, jbeyq!')
ok 3 - rot-13('I like pie')
1..3
ok
All tests successful.
Files=1, Tests=3,  0 wallclock secs ( 0.00 usr  0.00 sys +  0.00 cusr  0.01 csys =  0.01 CPU)
Result: PASS
$

Sweet.

The prove command is a TAP External link testing harness that is likely on your system.

Comments