testing

Testing tools, testing philosophy & methodology...
dottedmag 2017-10-15T18:32:00.000092Z

I have a network protocol to implement, which is can be described as a complicated state machine (Petri net, actually). I can easily slap together something, but how do I test it?

dottedmag 2017-10-15T18:33:06.000081Z

I have thought of inverting control, so that every step in a protocol is implemented as a pure function taking current state, event and producing new state and actions to perform (a-la Erlang gen_server), but then testing it would require crafting this internal state.

2017-10-15T18:33:25.000106Z

if you do the protocol using code in terms of InputStream and OutputStream making test data for tests is straightforward

2017-10-15T18:33:50.000010Z

oh, right, there's the back and forth thing

dottedmag 2017-10-15T18:34:52.000083Z

@noisesmith I'm afraid this is network protocol, not application one. It's a client for layer3 VPN tunnel running over DNS.

2017-10-15T18:34:56.000030Z

if I am testing a pure function, I always have to create the "state" it operates on

2017-10-15T18:35:19.000098Z

@dottedmag right that's fine, what I meant was that it takes bytes as input, and outputs bytes, at some point

dottedmag 2017-10-15T18:35:25.000112Z

That's why I hope there is another way, or a testsuite will be very brittle: any change in implementation would require changing all the tests.

2017-10-15T18:35:50.000018Z

well, it's a protocol, right? if the bytes change meaning you need to change tests...

dottedmag 2017-10-15T18:36:02.000083Z

Yes, but not the internal state.

2017-10-15T18:36:05.000106Z

if you mean implementation as in internal states ... OK you aren't using pure functions

2017-10-15T18:36:22.000038Z

you can make it pure by passing state as an argument (conventionally the first)

2017-10-15T18:36:31.000047Z

attach that to a driver that gives it bytes

2017-10-15T18:36:40.000132Z

could use the network, could use some data in a test

2017-10-15T18:37:30.000093Z

and yes, if the state changes implementation, your functions need to change!

2017-10-15T18:37:40.000140Z

and the tests of course

2017-10-15T18:38:18.000005Z

but you should be able to define "reasonable-resting-state" or whatever in the implementing code

2017-10-15T18:38:25.000048Z

it's part of the implementation clearly

2017-10-15T18:40:09.000039Z

yeah - it would make sense to make the initial state at the very least

dottedmag 2017-10-15T18:40:20.000027Z

They come from somewhere in the production — so maybe the same code should be exposed to the tests to produce the testing state.

2017-10-15T18:40:41.000038Z

and if you express the state right it should be straightforward to express it as a literal in the test via some assoc calls or whatever

dottedmag 2017-10-15T18:41:30.000034Z

E.g. something like (session-is-now-authenticated state) should be used both by a test which starts from an authenticated state, and by a protocol code itself once the handshake is complete.

2017-10-15T18:41:54.000076Z

also, I have a library for dumping app state (args, return values, whatever) into a document (via transit) and load it as a one liner in my test

dottedmag 2017-10-15T18:42:15.000027Z

That's a good way too. Though I wonder how readable the resulting tests are.

2017-10-15T18:43:16.000016Z

@dottedmag (let [app (restore-from-disk "./test/data/overcommit.transit.json"] (is (ok? (recover app))))

dottedmag 2017-10-15T18:43:31.000062Z

@noisesmith And how readable is overcommit.transit.json?

2017-10-15T18:43:48.000020Z

it's a transit file - on disk it's a json, in a repl you can do anything with it

dottedmag 2017-10-15T18:44:02.000039Z

I know, but can you really read it and understand the state the system in?

2017-10-15T18:44:04.000065Z

but the name should be informative at the very least 😄

dottedmag 2017-10-15T18:44:10.000014Z

ok 🙂

2017-10-15T18:44:12.000035Z

@dottedmag with a repl haha

dottedmag 2017-10-15T18:44:18.000043Z

Got it

2017-10-15T18:44:31.000030Z

so yeah, that is a concern to that approach, good point

dottedmag 2017-10-15T18:45:13.000017Z

All right, I'll start simple (with a handcrafted protocol state), try extracting common code to be used both by implementation and test code and see how it goes.

dottedmag 2017-10-15T18:45:41.000107Z

@noisesmith Thanks for thoughts.

2017-10-15T18:46:49.000061Z

@dottedmag one thing I do to offset the transit files not being especially human readable is making test assertions where the fact that the assertion passes tells you something about the nature of the thing stored

2017-10-15T18:47:00.000011Z

but that's not the same as being able to read the data of course