Skip to content

Commit e83fa83

Browse files
committed
blog: Add 2025-07-12.md
1 parent de1f2f2 commit e83fa83

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

blog/2025-07-12.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
---
2+
title: YS Testing
3+
date: 2025-07-12
4+
draft: false
5+
authors: [ingydotnet]
6+
categories: [Summer-of-YS]
7+
edit: blog/2025-07-12.md
8+
comments: true
9+
---
10+
11+
YS is a great language for writing tests in.
12+
13+
Why?
14+
15+
Well tests are mostly data.
16+
Inputs and outputs for some code you want to test.
17+
Code is usually in functions, and functions usually have names and names are
18+
just strings and strings are just data.
19+
20+
YAML is a great format for data.
21+
YS is YAML with code (expressed as YAML data).
22+
23+
So it seems like YS should be a great language for writing tests in.
24+
And it is.
25+
26+
<!-- more -->
27+
28+
29+
## Inline Tests in YS
30+
31+
Let's say we have a YS program called `rot13.ys`, and we want to have some tests
32+
for it inside the program.
33+
34+
We can do that like this:
35+
36+
```yaml
37+
#!/usr/bin/env ys-0
38+
39+
tests =::
40+
- in: Hello, world!
41+
out: Uryyb, jbeyq!
42+
- in: Uryyb, jbeyq!
43+
out: Hello, world!
44+
- in: I like pie
45+
out: V yvxr cvr
46+
47+
defn run-tests():
48+
each test tests:
49+
got =: test.in:rot-13
50+
if got == test.out:
51+
say: "PASS - rot-13('$(test.in)') -> '$got'"
52+
say: "FAIL - '$got' != '$(test.out)'"
53+
54+
defn main(input=nil):
55+
if input:
56+
say: input:rot-13
57+
run-tests:
58+
59+
dict =: set(\\A .. \\Z) + (\\a .. \\z)
60+
dict =: dict:cycle.drop(13 * 2).zipmap(dict)
61+
62+
defn rot-13(str):
63+
escape str: dict
64+
```
65+
66+
We can run the program like this:
67+
68+
```bash
69+
$ ys rot-13.ys 'rot-13 is cool'
70+
ebg-13 vf pbby
71+
$ ys rot-13.ys
72+
PASS - rot-13('Hello, world!') -> 'Uryyb, jbeyq!'
73+
PASS - rot-13('Uryyb, jbeyq!') -> 'Hello, world!'
74+
PASS - rot-13('I like pie') -> 'V yvxr cvr'
75+
$
76+
```
77+
78+
If we give it a string, it will print the ROT-13 of that string.
79+
80+
If we give it no arguments, it will run the tests.
81+
82+
The tests are just data and YAML is great for that.
83+
84+
85+
## The `ys::taptest` Module
86+
87+
Writing inline tests can be useful once in a while, but it's not the best way to
88+
write tests for a real project.
89+
90+
Let's morph that example into something more like a real project.
91+
92+
Program file `rot-13`:
93+
94+
```yaml
95+
#!/usr/bin/env ys-0
96+
97+
use rot-13: :all
98+
99+
defn main(input=nil):
100+
say: input:rot-13
101+
```
102+
103+
Library file `rot-13.ys`:
104+
105+
```yaml
106+
!YS-v0
107+
108+
ns: rot-13
109+
110+
dict =: set(\\A .. \\Z) + (\\a .. \\z)
111+
dict =: dict:cycle.drop(13 * 2).zipmap(dict)
112+
113+
defn rot-13(str):
114+
escape str: dict
115+
```
116+
117+
Test file `rot-13.t`:
118+
119+
```yaml
120+
#!/usr/bin/env ys-0
121+
122+
use rot-13: :all
123+
use ys::taptest: :all
124+
125+
test::
126+
- code: rot-13('Hello, world!')
127+
want: Uryyb, jbeyq!
128+
- code: rot-13('Uryyb, jbeyq!')
129+
want: Hello, world!
130+
- code: rot-13('I like pie')
131+
want: V yvxr cvr
132+
```
133+
134+
This code is much cleaner.
135+
We don't have to write testing logic because the YS builtin `ys::taptest` module
136+
takes care of that.
137+
138+
Let's run the program and the tests:
139+
140+
```bash
141+
$ ./rot-13 'Hello, world.'
142+
Uryyb, jbeyq.
143+
$ prove -v rot-13.t
144+
rot-13.t ..
145+
ok 1 - rot-13('Hello, world!')
146+
ok 2 - rot-13('Uryyb, jbeyq!')
147+
ok 3 - rot-13('I like pie')
148+
1..3
149+
ok
150+
All tests successful.
151+
Files=1, Tests=3, 0 wallclock secs ( 0.00 usr 0.00 sys + 0.00 cusr 0.01 csys = 0.01 CPU)
152+
Result: PASS
153+
$
154+
```
155+
156+
Sweet.
157+
158+
The `prove` command is a [TAP](https://testanything.org/) testing harness that
159+
is likely on your system.

0 commit comments

Comments
 (0)