bdd jasmine javascript labs mobile sencha

Sencha Touch BDD Part 2

Sencha Touch BDD

tl;dr

A multi-part series of articles on how to test Sencha Touch applications. It uses Jasmine for unit testing and Siesta for integration testing.

Part 2 – Unit Testing Models

In Part 1 I showed you how to set up your Sencha Touch development environment to use the Jasmine JavaScript test framework. We’re going to take a bit of a breather from all the hard work we did last week. In this blog, I’m going to show you how to test simple models.

Let’s have some fun, shall we?

Test-Driven-Development starts with a test, of course. Let’s write one that just asserts that our model class exists:

$ cat spec/javascripts/model/MyModelSpec.js
describe('SenchaBdd.model.MyModel', function() {
  it('exists', function() {
    var model = Ext.create('SenchaBdd.model.MyModel');
    expect(model.$className).toEqual('SenchaBdd.model.MyModel');
  });
});

When we run our tests in the browser, Jasmine reports this:

  Error: [Ext.Loader] Failed loading synchronously via XHR: 'app/model/MyModel.js';
  please verify that the file exists. XHR status code: 404

Which is just Ext’s very formal way of saying, “No such class exists” because we haven’t written it, yet.

Let’s write one that makes the test pass:

$ cat app/model/MyModel.js
Ext.define('SenchaBdd.model.MyModel', {
  extend: 'Ext.data.Model'
});

We have proven that we can create a new class, and that it’s name is what we expect.

Attributes

Let’s assert that our model has some attributes:

$ cat spec/javascripts/model/MyModelSpec.js
it('has data', function () {
  var model = Ext.create('SenchaBdd.model.MyModel', {
    name: 'Test',
    email: '[email protected]',
    favoriteColor: 'blue'
  });
  expect(model.get('name')).toEqual('Test');
  expect(model.get('email')).toEqual('[email protected]');
  expect(model.get('favoriteColor')).toEqual('blue');
});

Which of course, fails, until we add some fields to our model:

$ cat app/model/MyModel.js
Ext.define('SenchaBdd.model.MyModel', {
  extend: 'Ext.data.Model',
  config: {
    fields: [
      { name: 'name', type: 'string' },
      { name: 'email', type: 'string' },
      { name: 'favoriteColor' }
    ]
  }
});

Default values

Let’s say that our model has some default values

$ cat spec/javascripts/model/MyModelSpec.js
it('has default values', function() {
  var model = Ext.create('SenchaBdd.model.MyModel')
  expect(model.get('favoriteColor')).toEqual('yellow');
})

Reload the Jasmine runner in the browser and …

  SenchaBdd.model.MyModel has default values.
  Expected undefined to equal 'yellow'.

Which is easily resolved by adding it to the class:

$ cat app/model/MyModel.js
{ name: 'favoriteColor', defaultValue: 'yellow' }

Validations

One last simple piece, let’s assert that email is a required field.

$ cat spec/javascripts/model/MyModelSpec.js
it('requires an email address', function() {
  var model = Ext.create('SenchaBdd.model.MyModel');
  var errors = model.validate();
  expect(errors.isValid()).toBeFalsy();

  expect(errors.getByField('email')[0].getMessage()).toEqual('must be present');
})

The first expectation asserts that the model is not valid at all. It’s a gatekeeper test. The second test asserts that there’s validation error on the email field (and not some other field).

$ cat app/model/MyModel.js
  config: {
    ...
    validations: [
      { field: 'email', type: 'presence' }
    ]

Roundup

This is a pretty simple set of tests. If you are at all familiar with unit testing, you won’t find much new here. Testing for validations by inspecting on the Errors collection was a little tricky to suss out. Hopefully I’ve saved you a few frustrating moments digging through the source code. What’s also interesting, I think, is how the test report reads:

  SenchaBdd.model.MyModel
    exists
    has data
    has default values
    requires an email address

The test itself communicates something to the reader about the intention of the model. That’s a key concept to understand about TDD; the most important and expensive reader of the application is not the browser, it’s the person who reads and maintains it. It might be your successor or your team mate, or perhaps, yourself, six months from now, when you’ve forgotten everything about this particular patch of code.