Thus far we've been developing without tests which has been a necessary as we even attempt to map the landscape. However, it looks like very soon we will be transitioning from a pure research mode into actually having a product that will be able to do more and more useful and complex things. If so, then we'll need to have a way to automatically test it.
This change introduces a very minimal test suite that nevertheless tries to be as "Big" as possible. It starts the entire server before each test case, and then shuts it down after each test case, and only ever interacts with it over http.
In order to make this work I had to make some changes to the way the code is organized internally.
Parcel Bundler
It appears that Parcel does not shut down cleanly if stop() is called too soon after start(). After a delay of about 500ms, it will shutdown cleanly, but before that it leaks listeners and processes and hangs the instance of node from which it was invoked. I guess they didn't get the memo to use effection :)
To get around this, I just forked the bundler as a separate process which appears to work the same, but forces a clean shutdown, because parcel itself will invoke process.exit() to forcibly kill itself. This ought to be ok because under the covers, the Bundler#bundle() method is forking out three or four child processes anyway, so we're not realy saving anything by running the main bundler in-process.
The tradeoff is that we don't actually have a reference to the object so we can't know what its state of readiness is.
We might be able to figure out what's going on with Parcel and help it to shutdown cleanly and contribute that back to the project.
Logging
Another change I had to make was to the logging. With the amount of logging that we were doing in our testcases, the test console was really ugly. It's more of a workaround rather than anything else, but I delegated the console.log() function through an importable logger module that can be replaced at runtime in the test cases.
Hello World
Finally, to make sure that absolutely everything gets torn down properly after each testcase, everything async happens through a World object which is destroyed at the end. If we want to run an effection Operation, you would do it with world.fork() and it will be halted at the end. Even non-effection APIs should be adapted to happen in the World object, that way they too will be halted at the end. For an example of this, we can see the get() method which uses a promise.
Finally, the test suite itself is defined using mocha, but uses the strict Arrange/Act/Assert or Given/When/Then format. I imagine that we'll eventually want to dogfood our new syntax for expressing test suites, but that can come later.
Thus far we've been developing without tests which has been a necessary as we even attempt to map the landscape. However, it looks like very soon we will be transitioning from a pure research mode into actually having a product that will be able to do more and more useful and complex things. If so, then we'll need to have a way to automatically test it.
This change introduces a very minimal test suite that nevertheless tries to be as "Big" as possible. It starts the entire server before each test case, and then shuts it down after each test case, and only ever interacts with it over http.
In order to make this work I had to make some changes to the way the code is organized internally.
Parcel Bundler
It appears that Parcel does not shut down cleanly if
stop()
is called too soon afterstart()
. After a delay of about 500ms, it will shutdown cleanly, but before that it leaks listeners and processes and hangs the instance ofnode
from which it was invoked. I guess they didn't get the memo to use effection :)To get around this, I just forked the bundler as a separate process which appears to work the same, but forces a clean shutdown, because parcel itself will invoke process.exit() to forcibly kill itself. This ought to be ok because under the covers, the
Bundler#bundle()
method is forking out three or four child processes anyway, so we're not realy saving anything by running the main bundler in-process.The tradeoff is that we don't actually have a reference to the object so we can't know what its state of readiness is.
We might be able to figure out what's going on with Parcel and help it to shutdown cleanly and contribute that back to the project.
Logging
Another change I had to make was to the logging. With the amount of logging that we were doing in our testcases, the test console was really ugly. It's more of a workaround rather than anything else, but I delegated the
console.log()
function through an importablelogger
module that can be replaced at runtime in the test cases.Hello World
Finally, to make sure that absolutely everything gets torn down properly after each testcase, everything async happens through a
World
object which is destroyed at the end. If we want to run an effectionOperation
, you would do it withworld.fork()
and it will be halted at the end. Even non-effection APIs should be adapted to happen in theWorld
object, that way they too will be halted at the end. For an example of this, we can see theget()
method which uses a promise.Finally, the test suite itself is defined using mocha, but uses the strict Arrange/Act/Assert or Given/When/Then format. I imagine that we'll eventually want to dogfood our new syntax for expressing test suites, but that can come later.
TODOS / Open Questions