SugarTest makes it easy to write elegant and understandable JavaScript tests. Its API is inspired by both RSpec, Shoulda and jQuery. It works as a DSL running on top of JsUnitTest.Features:
- simple, expressive syntax
- setup and teardown routines
- nested contexts
- easy aliasing, use your own style
- no dependencies
- makes you fat
Check the SugarTest tutorial for usage information.
SugarTest has been created by Choan Galvez and is freely distributable under the terms of a MIT-style license. The source code resides in a Git repository at github. SugarTest Logo designed by Ale Muñoz, released under a WTFPL License. SugarTest is a fork of jShoulda featuring a revised syntax and new features.
Before we continue with SugarTest, I want to show you some examples of it in online casinos. Online casinos like you know require very strict programming without bugs. They need to be online 24/7 so any error can be devastating. So why don’t you get some of the promotional bonuses that allow you to play casino games for free, to make research on how the best online UK casinos are designed.
Example test
SugarTest()
.describe('A context')
.before(function(data) {
data.something = 1;
})
.it('runs its setup function', function(data) {
this.assertEqual(1, data.something);
})
.describe('which is an inner context')
.before(function(data) {
data.something += 1;
})
.it('runs both setup functions', function(data) {
this.assertEqual(2, data.something);
})
.end()
.end()
.run();
Live result
Status | Test | Message |
---|---|---|
A context runs its setup function | passed | 1 assertions, 0 failures, 0 errors, 0 warnings |
A context which is an inner context runs both setup functions | passed | 1 assertions, 0 failures, 0 errors, 0 warnings |
SugarTest Tutorial
SugarTest makes it easy to write elegant and understandable JavaScript tests. Its API is inspired by both RSpec, Shoulda and jQuery. It works as a DSL running on top of JsUnitTest.
It’s sweet.
Get started
Best way to get started is to download our sample package, which includes SugarTest, JsUnitTest and a sample test.
A minimal test document
Create an HTML document which references both jsunittest.js
and sugar_test.js
. Include a div
element with id
testlog, then your testing code.
TODO include example
The SugarTest API
SugarTest handles two concepts: contexts and units. Contexts define scenarios for testing and can be nested. Units define the actual test units. The API offers two styles:
- RSpec like, contexts are created with the
describe
method; units are created with theit
method. - Shoulda like, contexts are created with the
context
method, units are created with theshould
method.
You can mix both styles if you want, the only difference between them is the resulting test unit name.
Creating the test runner
Invoke SugarTest
, then run
its return value:
SugarTest().run();
Contexts and units
A context object is returned when invoking describe
or context
. You can call it
or should
on it to define test units. By invoking end
you get its parent context. By calling describe
you create a child context.
Calls to unit methods (it
, should
) must pass a name and a callback as arguments. Assertions are run inside the callback, where this
points to a Test.Unit
instance. These methods return the context they are invoked on.
This code will create a unit test named “SugarTest makes javascript testing sweet”.
SugarTest() // create a root context
.describe('SugarTest') // start a new context
.it('makes javascript testing sweet', function() { // define the unit test
this.assert(true); // assertions come from JsUnitTest
})
.end() // end the top context and get its parent
.run(); // add the defined tests to the test runner
Using the Shoulda style is just the same, but in this case the unit test would be named “SugarTest should make javascript testing sweet”.
SugarTest() // create a root context
.context('SugarTest') // start a new context
.should('make javascript testing sweet', function() { // define the unit test
this.assert(true); // assertions come from JsUnitTest
})
.end() // end the top context and get its parent
.run(); // add the defined tests to the test runner
Setting up, tearing down
You can use before
and after
on any context (including the root one) to define set up and tear down routines for each of its test units. The callback receives a store object which is also passed to test methods. this
points to the test unit object.
SugarTest()
.before(function(data) {
// `this` points to the test unit object
this.wadus = 'wadus';
data.blah = 'blah';
})
.after(function() {
// clean after yourself
})
.describe('Setup routines')
.it('are run before each test unit', function(data) {
this.assertEqual('wadus', this.wadus);
this.assertEqual('blah', data.blah);
})
.end()
.run();
The this
object is brand new for each test unit so you don’t need to clean up this
‘s properties.
Nested contexts
When nesting contexts, set up and tear down routines are run in order (outside-inside for before
, inside-outside for after
).
SugarTest()
.describe('A context')
.before(function() {
this.wadus = 'wadus';
})
.it('runs its set up routines', function() {
this.assertEqual('wadus', this.wadus);
})
.describe('when nested')
.before(function() {
this.wadus = this.wadus.toUpperCase();
})
.it('runs the chain of set up routines', function() {
this.assertEqual('WADUS', this.wadus);
})
.end()
.end()
.run();
You can call before
and after
as many times as you want in each context.
Alternatively, you can pass a function as the second argument to a context and get it added to the set up routines. Warning: This feature may be removed before reaching 1.0.
SugarTest()
.describe('A context', function() {
this.wadus = 'wadus';
})
.it('runs its setup routines', function() {
this.assertEqual('wadus', this.wadus);
})
.end()
.run();
Context data
You may want to use set up routines for sibling contexts that only differ in configuration data. Do it this way:
SugarTest('A something')
.before(function(data) {
this.something = new Something(data.someOptions);
})
.describe('with default configuration')
.data({ someOptions : {} });
it('behaves like a wadus', function() {
// assertions
})
.end()
.describe('with custom configuration')
.data({ someOptions : { hey: 'you' } })
.it('behaves like a hey-you-wadus', function() {
// assertions
})
.end()
.run();
Note that data
is constructed by merging the data objects of each parent context before running any set up routine.
Back to the root
You can skip multiple calls to end
by invoking root
, which allways return the root context.
SugarTest()
.describe(...)
.it(...)
.describe(...)
.it(...)
.describe(...)
.it(...)
.root()
.run();
The root context
You can pass a name to SugarTest
to use it as the basename. It includes context methods, you can call it
, before
, etc. on it.
SugarTest('SugarTest')
.it('could not be simpler', function() {
this.assert(true);
})
.run();
Remember: test units are not actually created untill you invoke run
on the root context.
Aliasing
It’s easy (and recommended) to define aliases for contexts an units methods. This way you can write and name your test in a quite natural language:
SugarTest
.setContextAlias('scenario', 'Scenario: %context')
.setContextAlias('feature', '%prefix, feature: %context.')
.setContextAlias('when', '%prefix When %context')
.setUnitAlias('then', '%context then %example');
SugarTest()
.scenario('scenario name')
.feature('feature name')
.when('I do something')
.then('something should happen', function() {
this.assert(true);
})
.root()
.run();
This would create a test case called “Scenario: scenario name, feature: feature name. When I do something something should happen”.
Warning: Aliasing methods may be reworked before reaching 1.0 version.
The test runner
Every unit created with SugarTest is added, by default, to a unique TestRunner. This way you can reuse the same runner for tests coming from different files. If you preferred using different runners, you’d have to call unifyRunners
with a false
argument.
SugarTest.unifyRunners(false);
You can pass options to the TestRunner by using an object as the last argument to SugarTest
.
SugarTest({ testLog: 'testlog-div-id' });
Assertions
Assertions come from the JsUnitTest library. Almost every assertion accepts three arguments: the expected value, the actual one and an optional message. An intentionally incomplete list of possible assertions follows:
- assert
- assertEqual
- assertNotEqual
- assertEnumEqual
- assertEnumNotEqual
- assertHashEqual
- assertHashNotEqual
- assertIdentical
- assertNotIdentical
- assertNull
- assertNotNull
- assertUndefined
- assertNotUndefined