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
testing harness that
is likely on your system.