{Expressive BDD Syntax}
var foounit = require('foounit');
describe('this is ta test', function() {
it('is only a test', function() {
expect(myTest).to(runEverywhere);
});
});
foounit.run();
describe, it, before, after, expect, mockTheres another class of foounit keywords for asynchronous testing, but these are explained in the async awesomeness section.
waitFor, run, waitForTimeout
These are the basic building blocks of foounit and asynchronous BDD style testing.
In BDD speak an it block is an example that defines usage of a particular feature. So let's say we want to test that a string is less than 10 characters. You might create an it block that looks like this:
it('returns true', function (){ var str = "sml_string"; // expect is assertion... don't worry about this for now, but this test will pass expect(str.length < 10).to(beTrue); });
So you might think that an it block is overkill for a test like this, but also realize that this is a contrived example. The nice thing about using it is that you are able to label this test with a description for the next developer that touches this code.
If you just want to play around with foounit to get an idea for how this will work, you can run something like this. Create a file called test.js with this code:
var foounit = require('foounit').globalize(); it('fails the test', function (){ expect(true).to(beFalse); }); foounit.run();Then run the file with this command:
$ node test.js
This test will fail, but it will give you an idea of what happens when you run an example.
Internally, describe creates a group of examples. All ths really means to you is that describe actually defines a particular behavior. Let me explain...
Let's say you have a signup form widget that allows you to submit a form when you have a password that is 8 or more characters, but it shows an error message if you have password that is less than 8 characters. You might have a test that looks like this:
describe('when the password is 8 or more characters', function (){ it('allows you to submit the form', function (){ ... }); }); describe('when the password is less than 8 characters', function (){ it('displays an error message', function (){ ... }); });
You can also nest describes if your code has nested behaviors (or nested if statements). Nested describes look something like this:
describe('when foo is not falsy', function (){ describe('when foo is an integer', function (){ it('returns true', function (){ ... }); }); describe('when foo is a boolean', function (){ it('returns false', function (){ ... }); }); }); describe('when foo is falsy', function (){ it('returns false', function (){ ... }); });
The expect keyword is just another way of creating an assertion. An assertion is a way to test that a particular value is what you expect that it should be. Here is a breakdown of how expect works:
expect( foo // actual value ).to( be // this is a === matcher... more on this later , 100 // expected value );
Here are some additional example expectations:
expect(1).to(beLt, 2); // passes expect(true).to(beFalse); // fails expect([1,2,3]).to(include, 2); // passes
Here are some examples of expectations that assert actual and expected do not match:
expect(1).toNot(be, 2); // passes expect(function (){ throw new Error('errar!'); }).toNot(throwError); // fails
A before block is something that sets up a particular test. A before block is run once for each group and they can be nested. before blocks are great for asserting that a test is setup properly before an example is run and for removing clutter from your tests. before blocks can be nested within describes and are run in order for each example.
describe('when foo is 1', function (){ var foo; before(function (){ // runs first foo = 1; }); it('does something', function (){ ... }); describe(when foo is 2', function (){ before(function (){ // runs second foo++; expect(foo).to(be, 2); }); it('does another thing', function (){ ... }); }); });
after runs after each test. It's great for cleaning up the environment and it runs even when the test fails.
Here is how to use before and after in conjunction to cleanup global variables:
describe('when the current user is bob', function (){ var origUser; before(function (){ origUser = global.currentUser; // save off currentUser global.currentUser = 'bob'; }); after(function (){ global.currentUser = origUser; // reset currentUser after the test runs }); it('does something', function (){ ... }); });
These keywords are 90% of what you need to write tests in BDD style using foounit. There is a lot more to foounit than just these keywords but you can get by without learning about additional matchers and asynchronousness if you are just experimenting. If anything in this guide is unclear, then please email the group.
var successCallback; before(function (){ successCallback = mock(function (data){}); $.ajax('http://localhost:5057/data.json', { success: successCallback }); }); it('gets the data', function (){ waitFor(function (){ expect(successCallback).to(haveBeenCalled); }); });
In this example, an xhr request is made to get the JSON data at http://localhost:5057/data.json. We have mocked the success callback and we wait for the response to succeed and call our callback to be called. If the request succeeds then the test will pass, but if the request is unsuccessful then the test will fail. The waitFor keyword waits for the function it is passed to run without failing. If the waitFor block fails, then it is retried until a timeout is reached. If the timeout is reached then the test fails and a kitten dies.
There's more to asynchronous testing than just waitFor but waitFor will generally get you pretty far. Another common use-case is to wait for an expectation to be met, then do something else and wait for another expectation. In this case you can use run to insert yourself into foounit's asynchronous execution queue. Here is an example:
it('does a lot of async stuff', function (){ var foo = ''; setTimeout(function (){ foo = 'bar'; }, 200); // Waits for foo to become 'bar' waitFor(function (){ expect(foo).to(be, 'bar'); }); // Runs after the waitFor above run(function (){ setTimeout(function (){ foo = 'baz'; }, 200); }); // Waits for foo to become 'baz' waitFor(function (){ expect(foo).to(be, 'baz'); }); });
foounit can also assert that something never happens with the use of waitForTimeout. These kinds of tests are generally frowned upon because they slow down a test suite, but sometimes they are valuable enough to offset the downside of slowing down the test suite.
waitForTimeout will run a function until a timeout is reached. The first time the function passes without error, waitForTimeout fails. Consider this example:
it('doesnt get to 1000 fast enough', function (){ var foo = 0 , inc = function (){ foo++; setTimeout(inc, 200); } setTimeout(inc, 200); waitForTimeout(function (){ expect(foo).to(beGt, 999); }); });
This example will fail because foo will not reach 1000 in the time that it takes for waitForTimeout to timeout.
Asserts that actual === expected.
expect(1).to(be, 1); // passes expect(undefined).to(be, null); // fails
Assert that actual is greater than expected.
expect(5).to(beGt, 4); // passes expect(1).to(beGt, 2); // fails
Assert that actual is === false.
expect(false).to(beFalse); // passes expect(null).to(beFalse); // fails
Assert that actual is falsy.
expect(false).to(beFalsy); // passes expect(null).to(beFalsy); // passes expect("test").to(beFalsy); // fails
Assert that actual is less than expected.
expect(1).to(beLt, 2); // passes expect(5).to(beLt, 4); // fails
Asserts that actual === null.
expect(null).to(beNull); // passes expect(undefined).to(beNull); // fails
Asserts that actual is truthy.
expect(1).to(beTruthy); // passes expect('').to(beTruthy); // fails expect(true).to(beTruthy); // passes
Asserts that actual === undefined.
expect(null).to(beUndefined); // fails expect(undefined).to(beUndefined); // passes
Assert that actual is deeply equal to expected. This is useful for saying that one object is the same as another but they are referencing different objects.
expect({ foo: ['bar'] }).to(equal, { foo: ['bar'] }); // passes expect({ baz: 1 }).to(equal, { baz: 2 }); // fails
Assert that an array has an element that matches type and object equality of expected.
expect([1, 2, 3]).to(include, 2); // passes expect([1, 2, 3]).to(include, 10); // fails
Assert that actual matches a regex.
expect('foo bar baz').to(match, /bar/); // passes expect('foo bar baz').to(match, /qux/); // fails
Asserts that the a function throws an error. The error message can be matched against to assert that the correct error message was thrown.
expect(function (){ throw new Error(); }).to(throwError); // passes expect(function (){ throw new Error('HEY NOW'); }).to(throwError, /Fuuuuuu/); // fails expect(function (){ /** I don't throw **/ }).to(throwError); // fails