-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathREADME
More file actions
162 lines (130 loc) · 6.38 KB
/
README
File metadata and controls
162 lines (130 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
This is TAP for Java!
There are currently two "releases" of sources.
Neither are for production use, but feel free to
give it a shot.
VERSION 0.1.6
./gen-embed CLASSNAME
=> import.java import statements
=> embed.java code to include in your class verbatim
VERSION 0.2 (ALPHA)
src/TapTest.java
can be used in a default package, a named package
(just put "org.rapidcow.test" or something...)
*or* a package-private class, by removing "public"
in front of the public class.
= What is TAP? =
TAP <https://testanything.org/> is a testing protocol.
Its syntax is perhaps best illustrated with an example:
$ java FrontendTests 4..4
1..7
# Start subtest 4
ok 1 - load songs.csv OK
ok 2 - speed 135 OK
ok 3 - show 5 OK
ok 4 - Five songs printed
ok 5 - Partition is 1st least loud song, at dB -12
not ok 6 # TODO Moana is 2nd least loud song, at dB -10
# Assertion 6 `TODO Moana is 2nd least loud song, at dB -10' failed:
# at FrontendTests$Tester.test4(FrontendTests.java:622)
# Verdict: element 1 of vector [
# Partition, How Far I'll Go - From Moana, Kills You Slowly,
# Talk (feat. Disclosure), Love Incredible (feat. Camila Cabello)
# ] != How Far I'll Go - From "Moana"
# got: java.lang.String - How Far I'll Go - From Moana
# expected: java.lang.String - How Far I'll Go - From "Moana"
ok 7 - The 3rd-5th least loud songs are at dB -9
# End of subtest 4
# Ran 7 tests and failed 0 tests.
# You still have 1 TODO test to go.
We specifically target Version 12.
= Why TAP? Why not JUnit5? =
TAP has this thing call a PLAN: should there be a System.exit(0)
somewhere, JVM will dutifully terminate with a successful $?.
The plan serves as a sort of checksum to protect against that.
It is possible to scan for /^Test run finished after / when
JUnit is executed with any --details other than "none", but
that requires more work than simply checking $?.
See also <https://www.nntp.perl.org/group/perl.qa/2009/03/msg12104.html>.
Another argument is the ability to run partial tests: each TAP test
is either a file interpreted by perl(1) (if the +x bit is not set)
or an execute (if, well, the +x bit is set). In fact, you don't
even need prove(1) to run the tests! I imagine it is possible to
run partial tests with JUnit too, but it definitely is not as
straightforward. (Of course, partial tests would undoubtedly be
made possible by splitting tests across different classes, then
use --select-class (-c) wisely (which I guess is what the creators
of JUnit intended test classes to be organized). The issue is that
my CS class sucks and forces us to store all tests in a single file;
so TAP tester would be my ticket out of this tyranny, not JUnit. ;)
An argument for me, personally, is the ability to keep the test alive
after one assertion fails. Personally, I would like to believe that
there is a reason behind every design choice; in this case, making
each failure makes it much less of a headache to test a sequence of
actions (object initialization, resource acquisition, socket connection
and whatnot) that depend on each other; in other words, stages of a
test suite that literally cannot continue because previous steps failed.
In that sense, JUnit tests are, in the deepest sense, "assertions",
because they immediately terminate the entire test with an error, so
that subsequent steps that depend on it are effectively skipped.
With TAP, you don't have to -- not to mention in many cases where
assertions are more like "this object has the correct state" and not
"this object exists", so they don't really interfere with each other.
That isn't to say you can't; run() would catch unchecked exceptions
and remind you to set an error status, which tells prove(1) to
consider your test a fail. So you have the choice to either raise
an exception (or they call it `throw' in Java... right) or skip a
portion of your test conditionally using skip(int, String, Object...):
// let's say `bee' is this dangerous resource that may fail
DangerousBee bee;
try {
bee = acquireBee();
}
catch (IOException e) {
bee = null;
}
// Option 1: exception
if (! ok (bee != null, "acquire Bee")) {
throw new IllegalStateException("ABORT! Bee was not acquired");
}
// then continue tests that require bee...
// Option 2: skip them
if (! ok (bee != null, "acquire Bee")) {
// tests that require bee
}
else {
skip (/* number of bee tests */, "did not acquire a bee");
}
the emphasis here is that you *can* and it's up to you to decide.
And that alone is enough reason to introduce Java to TAP for me. :)
= Who for? =
For me -- obviously. ;)
(I am aware of my excessive use of smilies; though contrary to what
you may be expecting, I will not apologize and you will have to put
up with it :)
Sorry, but that's the only way to explain why there are so many
ridiculous constraints around. The course staff in my CS class
don't really expect us to write our own libraries, so I'd have to
come up with nasty ways to "share" code between classes.
In general I look out for a few constraints:
* No "external" classes: code must compile even with the
dumbest possible `javac -cp .:../junit5.jar *.java' command.
This pretty much means the only context we may be used in are:
(1) embed the code directly and (2) a package-private class.
* No external dependencies (this is obvious.)
* Avoid states -- well, I used to. Now that they don't use a
signature checker anymore, this constraint is nullified.
* Avoid imports -- again, this is a previous constraint.
Not at all relevant; just look at how I use reflection
API recklessly... they don't bat an eye, I tell you.
The one time I had to use this on three different classes, each
inheriting from a previous one, I embedded a giant block of ~800
lines of private states and methods in the root class and let
inheritance do the rest. It was *insanely* successful.
And now we are here... and I want to do it again; though this
time (1) without inheritance and (2) without copying scattered
code between my projects and forgetting which one's the "best".
(That's just another way of saying I am versioning it!!)
= Copyright and things =
I wrote the code at the cost of my grades
so I deserve to be credited. :)
See copyright in COPYING.