My very first story was to create the landing page for my app which will have a status header on it. As the first story, the header content is static -- because no way of changing it has been developed yet. Despite being static, I still want a test for it and set about creating a test in Jasmine which would use Selenium to confirm the existence of this header and text within it.*
Surprising challenges stemming from an all-JavaScript environment and a non-Mocha choice in testing frameworks arose which made this far more time-consuming than expected.
So, for posterity, I present the steps required to get started.
Installing the Tools
Starting from my code on "the outside" of the environment, I first needed the Jasmine test framework in which to work:A core aspect of testing this behavior is loading up a browser and verifying that the header is indeed present. This means bringing in Selenium bindings that I can use within my Jasmine tests.
Theoretically one can leverage selenium-webdriver from a client page, however I will want to incorporate this into my automated testing and see no reason to avoid it. All of that is to say that I needed a Jasmine test runner for node.
FYI, in order to install this I needed to reconfigure my npm repository to http.
I have really come to enjoy using Koding for my development, because it lets me maintain my workspace across any machine with Internet access and a modern browser. However, as a hosted terminal-only VM, having Selenium try to pop open Firefox and Chrome won't exactly work. So I brought in PhantomJS for headless browser testing.
Notes on the Tests
Working with selenium-webdriver is pretty frustrating at times, because it often fails in complete silence leaving you floundering for answers.Here are the critical items I found this way:
- The documentation shows three styles of test writing: one in Java, a second using continuation passing (i.e. callbacks) and a third using promises. Promises in selenium-webdriver are apparently not fluency sugar, promises are required! Expect a long, but readable, chain of "then" methods.
- findElement does not pass undefined if nothing is found, it instead "rejects" the promise and flows to the error handler.
- Tip: instead of "expecting false to be true" in order to force a failed expectation in the error handler, I suggest "expecting the error not to be defined." This has the added benefit of outputting the error object's contents in the test log.
Get it Running
Lastly fire up Selenium and set jasmine-node loose on the tests. For bonus points, it can be set it to automatically rerun the tests when changes are detected in the specs (i.e. the tests) or the code file(s).The Future
While selenium-webdriver is capable of launching the stand-alone server from code, I have yet to figure it out and am living with one additional command (OH NOOOES!).*It "appearing" constitutes a behavior which adds value according to a user story, and so it should be protected against regression.