Testing
1. Introduction to Testing
Testing is core to any great software product and Batman.Testing aims to make testing easy in Batman. Batman.TestCase and its subclasses are light-weight wrappers around QUnit and Sinon.JS libraries with the goal of making testing your JS MVC application part of your day to day practices.
2. Testing with Batman.TestCase
Batman.TestCase is the base class all of your Batman App’s tests will derive from. TestCase provides a simple API of common assertions you’ll want to perform in your tests. In most cases, these methods are simple wrappers around their QUnit equivalents but with a focus on making it easy for Rails developers to write these tests. The testing assertions are modeled after Ruby’s Test::Unit, bringing in line expectations for expectation and actual assertion values as well as method signatures.
2.1 Class setup
To create a basic Batman test, simple create a new class that extends from Batman.TestCase.
class SimpleTest extends Batman.TestCase
@test 'A simple test', ->
@assert true2.2 Setup and Teardown
All Batman.TestCase tests are given setup and teardown methods that are called before each test is run. Use these methods for initializing test data or resetting values that may persist between tests.
class SimpleTest extends Batman.TestCase
setup: ->
@foo = new Batman.Object(bar: 'Hello')
teardown: ->
window.badExample = false
@test 'A simple assertion', ->
@assertEqual true
window.badExample = true
@test 'No bad examples', ->
@assert !window.badExample2.3 Assertions
Batman.TestCase comes with a set of basic assertions that Ruby Test::Unit users will be familiar with:
assert(bool, [msg]): Ensures the expression is true
assertEqual(expected, actual, [msg]): Ensures that expected is deepEqual to actual
assetNotEqual(expected, actual, [msg]): Ensures that expected is [notDeepEqual] (http://api.qunitjs.com/notDeepEqual) to actual
assertMatch(expected, actual, [msg]): Ensures that expected:Regex matches actual:String.
assertNoMatch(expected, actual, [msg]): Ensures that expected:Regex does not match actual:String.
assertDifference(expressions, difference = 1, [message], callback): Ensures that the set of expressions (single, or Array) have a delta of difference after the callback is executed.
assertNoDifference(expressions, difference = 1, [message], callback): Ensures that the set of expressions (single, or Array) have no delta after the callback is executed.
assertRaises(exception, callback): Ensures that an exception of type exception is raised during the executing of the callback.
2.4 Async tests
If your tests are asynchronous, you can control the flow of them with the continue and wait functions. These are simple wrappers over QUnit’s start and stop methods.
class SimpleTest extends Batman.TestCase
@test 'something asynchronous', ->
setTimeout =>
@testCase.assert true
@testCase.continue()
@testCase.wait()2.5 Running your tests
Batman.TestCase test methods simply wrap QUnit’s existing test method. In order to run your tests, instantiate an instance of your Batman.TestCase and call runTests. This will add a new QUnit.module with your given test class name and queue all tests in the class into QUnit’s test runner.
test = new SimpleTest
test.runTests()3. Unit testing your Models
To create a unit test for a Batman.Model in your app, create a test class that extends Batman.ModelTestCase.
class App.PostsTest extends Batman.ModelTestCaseEverything available to Batman.TestCase will be included in your Batman.ModelTestCase as well as additional, Model specific assertions and helper functions.
3.1 Batman.ModelTestCase specific assertions
assertValid: (model, [msg]): Ensures the given model has no Batman validation errors
assertNotValid: (model, [msg]): Ensures the given model has Batman validation errors
assertDecoders: (modelClass, keys...): Ensures the given model class has a set of decoders equal to the keys specified.
assertEncoders: (modelClass, keys....): Ensures the given model class has a set of encoders equal to the keys specified.
assertEncoded: (model, key, expected): Ensures the given model encodes the key property with a value equal to the expected value.
3.2 Fixtures
There is currently no fixture framework included with Batman.ModelTestCase however a simple fixture approach can be achieved by defining simple POJOs in this manner:
Fixtures.Posts =
newPost:
id: 1
title: "Hello world!"
handle: "new-post"
body: "Hello to all those people."and using them in your test classes.
class App.PostsTest extends Batman.ModelTestCase
setup: ->
@post = App.Post.createFromJSON(Fixtures.Posts.newPost)
@test: ->
@assertEqual "Hello world!", @post.get('title')3.3 A few examples
class App.Post extends Batman.Model
@encode 'title', 'body'
@encode 'handle',
decode: (val) -> 'post-' + val
encode: (val) -> val.slice(5)
@validate 'title', presence: true
class App.PostTest extends Batman.ModelTestCase
setup: ->
@post = App.Post.createFromJSON(Fixtures.Posts.newPost)
@test "has proper decoders", ->
@assertDecoders App.Post, 'id', 'title', 'body', 'handle'
@test "has proper encoders", ->
@assertEncoders App.Post 'title', 'body', 'handle'
@test "custom handle decoder adds 'post-' prefix", ->
@assertEqual @post.get('post-new-post')
@test "custom handle encoder removes 'post-' prefix", ->
@assertEncoded @post, 'handle', 'new-post'
@test "title can't be blank", ->
@post.unset 'title'
@assertNotValid @post4. Functional tests for your Controllers
More to come. Currently you can use a combination of Sinon.JS and Batman.TestCase to add functional tests to your controllers. A Batman.ControllerTestCase is on the way.
