I’ve had this question come up a few times recently, and I wanted to get everybody’s take on it… when you write BDD style tests, what practice do you prefer?
…I’ll have a follow up to this poll in a few days and explain some of my own reasoning to the whole situation.
After installing Erlang on OS-X and then getting QuickCheck installing via Erlang, I wanted to expand the OS options I’m using to Ubuntu (i.e. Linux). So in this entry I’m going to cover the Erlang install, a quick eunit bit, and then a QuickCheck install and sample. The first step in geting Erlang installed is deciding how you want to install it. So far, it isn’t like Rails where it is pretty important which method you pick to how well it will work for you on Ubuntu. For Erlang all methods get you started with a good version that is working. The method I used was simply to install with apt-get.
After installing, always a good idea to run things and make sure they’re all content and happy with your machine. Startup the erlang shell.
[sourcecode language=”bash”]
erl
[/sourcecode]
Then run some of the commands. Some I’ve found that will present you with useful and interesting information ist he erlang:system_info function with appropriate parameter passed. The otp_release parameter will get the version of erlang, the cpu_topology shows you the processor outlay for you machine (or in this case my virtual machine, with a single processor core allocated to it), and allocated_areas shows a bit about system memory allocations.
Now that erlang is effectively installed we can write a little sample code. To do this I created a directory called “TestingErlang” and in it placed an Erlang code file called “eunit_tests.erl”. Note: I’m using Sublime 2 on Ubuntu, so exchange that for whatever text editor you’re using for your Erlang coding.
Adding the header file include will cause all the function with _test() or _test_() to automatically be exported. An exported function of test() is created that can be used for running all of the unit tests. This also will include the preprocessor macros of EUnit for writing tests. So now throw a super simple test into the file.
You may want to, amid the automatic export of the methods ending in _test() or _test_() not name them this, and you’ll then need to add a line at the top of your code file like this.
BOOM! Passing. So now we know we have a good Erlang install and eunit is setup and usable. In the following blog entries I have in the works, we’ll dive deeper into what and how Erlang works from an extremely basic level all the way to diving into some of the more complex features.
A few weeks ago I kicked off this series of “Distributed Coding Prefunc: Up and Running with Erlang” and had wanted to keep up the momentum, but as life goes I had to tackle a few other things first. But now, it’s time to get back on track with some distributed computing. I intend to write tests with my samples, as I often do, I decided to take a stab at .
Before going forward, note that there is QuickCheck for Haskell and there is a QuickCheck for Erlang. Since the point of this “Distributed Coding Prefunc” is to get started coding with Erlang from zero, I’ll be talking about the Erlang version here. This version is created by John Hughes and Koen Claessen, starting the Quviq Company in 2006.
To download QuickCheck choose the version you intend to use, I’ve chosen the commercial license version from the download page.
At the command prompt, install QuickCheck by running Erlang and then run the install with these commands.
If the execution of the install displays this error, you’ll need to use sudo.
[sourcecode language=”bash”]
Installing ["pulse-1.27.7","eqc-1.27.7","eqc_mcerlang-1.27.7"].
Failed to copy pulse-1.27.7–copy returned {error,eacces}??
** exception exit: {{error,eacces},"pulse-1.27.7"}
in function eqc_install:’-copy_quickcheck/3-lc$^0/1-0-‘/3 (../src/eqc_install.erl, line 63)
in call from eqc_install:install2/4 (../src/eqc_install.erl, line 44)
[/sourcecode]
Kill Erlang with a ctrl+c and restart Erlang with the sudo command.
Now when you install you should see the following result or something similar. You’ll be asked to continue, select lowercase ‘y’ to continue. It may be different for some, but when I hit uppercase ‘Y’ (I suppose I got overzealous to install QuickCheck) it finished as if I’d hit no or something else.
[sourcecode language=”bash”]
1> eqc_install:install().
Installation program for "Quviq QuickCheck" version 1.27.7.
Installing in directory /usr/local/lib/erlang/lib.
This will delete conflicting versions of QuickCheck, namely
[]
Proceed? y
Installing ["pulse-1.27.7","eqc-1.27.7","eqc_mcerlang-1.27.7"].
Quviq QuickCheck is installed successfully.
Looking in "/Users/adronhall"… .emacs not found
Could not find your .emacs file!
Try install("path-to-emacs-file") or install(new_emacs).
Bookmark the documentation at /usr/local/lib/erlang/lib/eqc-1.27.7/doc/index.html.
ok
[/sourcecode]
You’ll note above, I don’t currently have emacs installed. The reason it looks for emacs is because QuickCheck has templates/ops mode for emacs. So if you use emacs you’re in luck. I on the other hand, don’t, so I’ll just be using this from wherever I’m using it.
In addition to the lack of emacs, another important thing to note from the message is the link to documentation. Once you get this link open it up and check out the docs. They’re broken out into easily readily topic spaces and are a good place to do initial reference checking while you’re writing up your specs.
If you have a license, it is important to note, that if you’ve used sudo with your installation you’ll need to kill your running Erlang session and start it anew without sudo. Otherwise you’ll run into issue down the road trying to use the libs (unless of course you want to go hack on your permissions manually). Once you’re ready to register the software it’s simply one command, where xxxxx is your license key.
In the last blog entry I wrote up vows.js for testing JavaScript, in this I’ve tried out another testing framework called mocha. This framework is pretty extensive as you can do the things you do with vows.js as well as a lot of other techniques. In addition to mocha I’ve added a few other things to the mix. As well as a few obvious points where I need to RTFM still about how mocha works.
mocha.js
Before falling off into a conversation about reading the manual, I’ll dive into a bit about mocha. Mocha is a project, hosted on github as you might expect, that aims to provide a very feature rich test framework that can run via node or the browser. It also enables asynchronous and synchronous testing with some pretty sweet reporting.
The installation is the standard simplicity of a beautiful and elegant package from npm for node.js.
Of course, depending on the way you’ve setup your machine, you may need to hit that command with sudo. The sample test on the main documentation & project page is pretty straight forward, I’ve copied it below for easy reference.
Note that above, where “$EDITOR” is whatever editor you’re using, such as Sublime 2, Textmate, Webstorm or whatever.
[sourcecode language=”javascript”]
var assert = require("assert")
describe(‘Array’, function(){
describe(‘#indexOf()’, function(){
it(‘should return -1 when the value is not present’, function(){
assert.equal(-1, [1,2,3].indexOf(5));
assert.equal(-1, [1,2,3].indexOf(0));
})
})
})
[/sourcecode]
Then run the test.
[sourcecode language=”bash”]
$ mocha
.
✔ 1 test complete (1ms)
[/sourcecode]
Overall, pretty sweet and simple. There are ways to setup mocha to do TDD or BDD style also. What I’ve done for my basic name generator I’m building has started of super simple. One of the other additions that I’ve added below is the should.js library. I’ll add more about this library, and the intent behind using it in the next blog article I write up.
My first tests I’ve put together I’ve entered below.
[sourcecode language=”javascript”]
require(‘should’);
var factory = require(‘./../lib/NameFactory’).nameFactory;
describe(‘when working with names using NameFactory’, function(){
var generateThisManyNames = 3;
describe(‘generating names’, function(){
it(‘should return a name between 3 and 20 characters’, function(){
factory.generate_names().length.should.be.approximately(3,20);
});
it(‘should return correct number of names’, function(){
factory.generate_names(generateThisManyNames).length.should.equal(generateThisManyNames);
});
})
});
[/sourcecode]
…and then I’ve implemented the absolute basic to get those tests to pass. You JavaScripters that have been at it for a while can likely detect my massive newbism among this code. I’d love feedback btw. 🙂 So here’s my basic implementation so far.
I’m still sketch on best practices around a number of object uses and creation in JavaScript. For instance in my code above, I’m creating a module, and setting up properties with functions, which leads to a number of questions… (maybe it is stack overflow time)
Is this an efficient way to create a JavaScript function for returning randomly created names?
What would your first tests be when creating a name generator? Any suggestions on some tests to add?
This shows some of my intent, such as returning a single result if no count is entered or it is less than 2, any suggestions on doing a kind of overloaded factory pattern method like that?
Now that I’ve gotten what I do above, I need some more tests to add to confirm that the returned content isn’t [”,”,”] <- cuz’ obviously that’s useless and not random, just returns the 3 string array that would prospectively have generated (or randomly selected) names. Ideas?
Is there another way to create a class or namespace that isn’t “module.export…” like I have above with “module.exports.nameFactory”? I’d love to just have a “NameFactory” or something. Not sure how or what way would be best in JavaScript land to put something like that together.
I’ve already moved past just this, but would love any feedback on the above questions and code snippets. I’ll post my results and the changes and additions from any body that posts feedback and suggestions in a subsequent post. 🙂
NOTE: All parts of this series and my other series can be found on my series links page.
I sat down a few days ago on this trip to get a simple project setup, instead something else happened. It is my fault, entirely my fault. I could have stayed focused on a single library but instead I’ve let them explode. They’re everywhere now in my project! 😮 I’ve learned a great deal about breaking and fixing things though when working with testing frameworks and Node.js. All of this being rooted in my exceedingly simple project called NameFactory on Github. Here’s a few of the things I’ve stumbled through over the course of figuring out these testing frameworks.
vows.js (while chillin’ at Phils Coffee)
Phils Coffee, a staunch cup indeed!
If you aren’t familiar with vows, here’s the brief description. Vows is a node.js testing framework that is built around the notion of BDD, Behavior Driven Development. The other important thing to note, is vows.js was built specifically to test asynchronous code. It can test others, but it is more oriented toward asynchronous code. Vows also runs tests in parallel, which thinking about that it takes the random order execution and ups the game even further by running them this way. It can really tighten up code execution and regression that things will indeed work in many different conditions. Building for web scale on the internet, that’s something we definitely need! 🙂
To install vows, it follows your standard pattern with npm:
From there I spun up a really simple example that is shown on the vows.js site:
[sourcecode language=”javascript”]
var vows = require(‘vows’),
assert = require(‘assert’);
vows.describe(‘Division by Zero’).addBatch({
‘when dividing a number by zero’: {
topic: function () { return 42 / 0 },
‘we get Infinity’: function (topic) {
assert.equal (topic, Infinity);
}
},
‘but when dividing zero by zero’: {
topic: function () { return 0 / 0 },
‘we get a value which’: {
‘is not a number’: function (topic) {
assert.isNaN (topic);
},
‘is not equal to itself’: function (topic) {
assert.notEqual (topic, topic);
}
}
}
}).run();
[/sourcecode]
From this example you run the tests by executing the code with the following command:
This is all fine, but I’d rather be using the vows test runnner. To do that you’ll need to, instead of *.run(); call the *.export(); function instead. At this point I started scratching my head a bit and looking for parallels between my understanding of BDD and testing and what vows is doing. So I read further and came up with a few observations.
In vows the convention is to have one test suite per file and have the subject match the name. This makes sense to me, that’s how I’ve generally done similar things with Ruby, C# and the like. You end up with something like this for setting the subject and adding the various test suites:
[sourcecode language=”javascript”]
var suite = vows.describe(‘subject’);
suite.addBatch({/* run 1st */}).addBatch({/* 2nd */}).addBatch({/* 3rd */});
suite.addBatch({
‘A context’: {
topic: function () {/* Do something async */},
‘I am a vow’: function (topic) {
/* Test the result of this topic */
}
},
‘Another context’: {
topic: function () {/* Do something else async */},
‘I am another vow’: function (topic) {
/* Test the result of this topic */
},
‘I am another sub-context vow’: function (topic) {
/* Test the result of the topic */
},
‘And another sub-context’: {
/* Executed when the tests above finish running */
}
}
});
[/sourcecode]
Each of the topics are the thing that is being tested with the secondary element of each test being the test to verify the topic. Each of these things are run asynchronously, in parallel, so be sure to keep that in mind when using this framework. However note, in the sub-context tests above the sub-context happens in order. This can be used in various ways, but be sure to not abuse this execution model to avoid the asynchronous nature of JavaScript, I’ve only been working with vows for a few days and have already seen code snippets on the web that have done exactly that!
So this is the first part in a series where I’ll just cover all sorts of JavaScript libraries. It took a Coder’s Vacation however to get me started on this series, enjoy.
You must be logged in to post a comment.