Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Ken Mayer

Sencha Touch BDD – Part 5 – Controller Testing

Ken Mayer
Saturday, May 18, 2013

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 5 – Controller Testing

Recap

Part 4 Introduced PhantomJS as an easy and faster alternative to headful Jasmine testing. Part 3 added jasmine-ajax so we can verify that stores and models react properly to back-end data. We also learned how to use stores to test views, without depending on a back-end server. In Part 2 I showed you how to unit test Sencha model classes in Jasmine. In Part 1 I showed you how to set up your Sencha Touch development environment to use the Jasmine JavaScript test framework.

It’s a control thing, but I will let you understand

Sencha Touch controllers usually live within the context of a single application object. Normally, this is handled for you when you invoke Ext.Application() in your app.js file. It creates a singleton object for you in the namespace of your application. For example, if you configured your application’s name to be ‘SenchaBdd’, then the application will be available as the .app attribute of the global SenchaBdd object, that is, SenchaBdd.app.

Unit testing should not have a running application, however. The point is that we are testing classes in isolation. There’s nothing isolated about an integrated, running, Javascript application. There is a relatively simple solution, however; You need to create you own “test” application object that you can then pass as a configuration option when you create controllers under test.

$ cat spec/javascripts/controller/MyControllerSpec.js
describe('SenchaBdd.controller.MyController', function() {
    var controller, app;
    beforeEach(function () {
        app = Ext.create('Ext.app.Application', {name: 'SenchaBdd'});
        controller = Ext.create('SenchaBdd.controller.MyController', { application: app });
        controller.launch();
    });

    afterEach(function() { app.destroy(); })

You may want to refactor the application creation and tear-down into a spec helper, to DRY out your tests.

Test behaviors, not events

It’s tempting to write a Jasmine test that tries to trigger an event in the DOM, then follow the event handling through the application. This is the road to hell. If you find yourself trying to simulate an event, please stop. That is what integration tests are better at doing. Controllers are classes like any other, and you should test methods in the same way. For example, let’s drive out a behavior where, when a user taps on the ‘Buy’ button our application sends a request to the back-end.

describe('SenchaBdd.controller.MyController', function () {
  var controller, app;
  beforeEach(function () {
    app = Ext.create('Ext.app.Application', {name: 'SenchaBdd'});
    controller = Ext.create('SenchaBdd.controller.MyController', { application: app });
    controller.launch();
  });

  afterEach(function () {
    app.destroy();
  });

  it('#newOrder', function () {
    var order = controller.newOrder();
    expect(order.$className).toEqual('SenchaBdd.model.MyModel');
    expect(order.phantom).toBeTruthy();
  });

  describe('#onBuy', function() {
    it('calls save on the order', function() {
      var myOrder = Ext.create('SenchaBdd.model.MyModel');
      spyOn(myOrder, 'save');
      spyOn(controller, 'newOrder').andCallFake(function() {
        return myOrder;
      });
      
      controller.onBuy()
      
      expect(myOrder.save).toHaveBeenCalled();
    })
  })
});

You might notice that I neither ‘tap’, nor do I test for a ‘POST’ ajax call. The former is better tested through integration tests. The latter is better tested in the model. All the controller need do assert that the model was saved. We trust external classes to function (because they’re tested, too, right?) Testing the #save method on the model follows the same process as testing stores, as I outlined in Part3. Another thing to note is that, under test, this controller does not have any views associated with it; Ext.ComponentQuery calls will return empty (undefined) results. This is to be expected in an isolated test, but may make for some head scratching when you first encounter it. If you must test something in the DOM, you should be writing an integration test anyway.

Ext.define('SenchaBdd.controller.MyController', {
  extend:   'Ext.app.Controller',
  config:   {
    views:   ['MyView'],
    refs:    {
      buyButton: 'myview #buyButton'
    },
    control: {
      buyButton: { tap: 'onBuy' }
    }
  },
  newOrder: function () {
    return Ext.create('SenchaBdd.model.MyModel');
  },
  onBuy:    function () {
    this.newOrder().save();
  }
});

As a personal preference, and make it easier to test and refactor, I have a #newOrder method that delegates to the model to create a new instance.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Sencha Touch BDD – Part 4 – PhantomJS

Ken Mayer
Friday, May 10, 2013

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 4 – Headless testing using PhantomJS

Part 3 added jasmine-ajax so we can verify that stores and models react properly to back-end data. We also learned how to use stores to test views, without depending on a back-end server. In Part 2 I showed you how to unit test Sencha model classes in Jasmine. In Part 1 I showed you how to set up your Sencha Touch development environment to use the Jasmine JavaScript test framework.

I hear it’s all about the cloud these days

Not only do we use Test Driven Development, we test all the time (see also TATFT). In fact, continuous testing is a key component in a well-run agile practice. Pivotal developed ciborg (previously known as Lobot), to automatically deploy Jenkins servers in the cloud. Jasmine has a continuous integration target in its rake tasks, rake jasmine:ci. If you’ve never run it before, now is a good time to try it. You’ll notice that Jasmine launches an instance of the Firefox browser, runs the test suite, then reports the results. That’s great if you’ve got (1) Firefox and (2) a machine that has a display! When you run your CI tests in the cloud, the virtual machines may not be configured with a virtual display. Furthermore, the process is terribly slow.

In this installment, we’ll see how to mix in PhantomJS so we can run our Jasmine tests without the need for a visual browser. PhantomJS is orders of magnitude faster, too.

Do the install dance

  1. Add ‘jasmine-phantom’ to your Gemfile
index e6b5daf..612fb25 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,3 +2,4 @@ source "https://rubygems.org"
 
 gem "rake"
 gem "jasmine"
+gem "jasmine-phantom"

and modify your Rakefile to load the PhantomJS tasks:

--- a/Rakefile
+++ b/Rakefile
@@ -2,6 +2,7 @@
 begin
   require 'jasmine'
   load 'jasmine/tasks/jasmine.rake'
+  load 'jasmine-phantom/tasks.rake'
 rescue LoadError
   task :jasmine do

Finally, run bundle install

Now, when you run rake -T from the command line, you’ll see a new target:

$ rake -T jasmine
rake jasmine             # Run specs via server
rake jasmine:ci          # Run continuous integration tests
rake jasmine:phantom:ci  # Run jasmine specs using phantomjs and report the results

Run rake

Running it will the phantom:ci target will start the Jasmine continuous integration tests in PhantomJS instead of Firefox.

$ rake jasmine:phantom:ci 2>1 /dev/null
[2013-05-06 20:28:15] INFO  WEBrick 1.3.1
[2013-05-06 20:28:15] INFO  ruby 1.9.3 (2012-11-10) [x86_64-darwin12.2.0]
[2013-05-06 20:28:15] INFO  WEBrick::HTTPServer#start: pid=17061 port=60080
Waiting for jasmine server on 60080...

8 specs | 0 failing
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Sencha Touch BDD – Part 3 – Testing Views and Mocking Stores

Ken Mayer
Sunday, May 5, 2013

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 3 – Testing Views and Mocking Stores

In Part 1 I showed you how to set up your Sencha Touch development environment to use the Jasmine JavaScript test framework. In Part 2 I showed you to unit test Sencha model classes in Jasmine.

I don’t normally test views, but when I do

There’s an old MVC mantra: “Fat models, skinny controllers and stupid views.” We don’t want complex logic in our views; that makes them hard to maintain. We don’t want any business logic in our controllers, either. That’s why we have models. Views should be so simple that they don’t require tests. There is a gray area with Sencha, however, where we found testing to be useful in our design process. It has to do with how views interact with stores.

Stores are essentially collections of models. They also encapsulate the persistence layer logic, separate from the business logic of the model. DataViews are a special class of Views in Sencha Touch that will consume a collection to generate a list-type view via a template.

Here are two goals for testing views and stores:

  • Does the view consume the right fields from the store, without hitting the back-end for data?
  • Does the store organize the data from the back-end, i.e. create the interface that is used by the view? Again, without hitting the back-end for data.

Small steps and iterate

Let’s define a very simple view via tests, we’ll make it work using an in-line store, then we’ll refactor the store into its own class. Next, we’ll refactor the storage class to use a remote back-end.

Ext.require('SenchaBdd.view.MyView');

describe('SenchaBdd.view.MyView', function () {
  it("has a list of colors", function () {
    var view = Ext.create('SenchaBdd.view.MyView', {
      renderTo: 'jasmine_content',
      store:    {
        fields: ['color'],
        data:   [
          {color: 'red'},
          {color: 'green'},
          {color: 'blue'}
        ]
      }
    });

    expect(Ext.DomQuery.select('.favorite-color').map(function (el) {
      return el.textContent
    }).join(', ')).toEqual('red, green, blue');

  });
});

Sencha does not come bundled with jQuery, so if you were expecting a DOM query like, $(“.favorite-color”), you might be surprised by the expectation. Ext has its own flavor of querying the DOM, using Ext.DomQuery.select.

Try implementing the view on your own to make the test pass. It should look remarkably similar to this:

Ext.define('SenchaBdd.view.MyView', {
  extend: 'Ext.dataview.DataView',
  xtype:  'myview',
  config: {
    itemTpl: '<div class="favorite-color">{color}</div>'
  }
});

In your application, you probably won’t hardwire a store into the view. In fact, you will probably embed this little view inside a larger container, like so (this adds another tab to the sample app):

--- a/app/view/Main.js
+++ b/app/view/Main.js
@@ -10,6 +10,11 @@ Ext.define('SenchaBdd.view.Main', {
 
         items: [
+            {
+              title: 'Favorites',
+              iconCls: 'star',
+              xtype: 'myview',
+              store: 'mystore',
+              styleHtmlContent: true
+            },
             {
                 title: 'Welcome',
                 iconCls: 'home',

Let’s create a store so our view will show us something:

Ext.define('SenchaBdd.store.MyStore', {
  extend: 'Ext.data.Store',
  config:    {
    storeId: 'mystore',
    fields: ['color'],
    data:   [
      {color: 'red'},
      {color: 'green'},
      {color: 'blue'}
    ]
  }
});

In order to see this view, we’ll need to add it to our app.js file:

--- a/app.js
+++ b/app.js
@@ -31,7 +31,11 @@ Ext.application({
     ],
 
     views: [
-        'Main'
+        'Main', 'MyView'
     ],

+    stores: [
+            'MyStore'
+    ],
 
     icon: {

Now, let’s go back and refactor our test so it uses MyStore instead of one that was hard-wired.

--- a/spec/javascripts/view/MyViewSpec.js
+++ b/spec/javascripts/view/MyViewSpec.js
@@ -2,17 +2,16 @@ Ext.require('SenchaBdd.view.MyView');
 
 describe('SenchaBdd.view.MyView', function () {
   it("has a list of colors", function () {
+    var store = Ext.create('SenchaBdd.store.MyStore', {
+      data:     [
+        {color: 'red'},
+        {color: 'green'},
+        {color: 'blue'}
+      ]
+    });
     var view = Ext.create('SenchaBdd.view.MyView', {
       renderTo: 'jasmine_content',
-      store:    {
-        fields: ['color'],
-        data:   [
-          {color: 'red'},
-          {color: 'green'},
-          {color: 'blue'}
-        ]
-      }
-
+      store:    store
     });
     expect(Ext.DomQuery.select('.favorite-color').map(function (el) {
       return el.textContent

All of our tests should remain green, but we’ve removed the “fake” store from our test.

Let’s get dynamic

Static data stores are boring. The fun starts when you start talking to a back-end API. Let’s say that we have a server that responds to an end point of ‘/colors.json’ with a list of favorite colors. We can even “fake” it by placing a file in the appropriate place. Even so, we don’t want our tests to make network calls to the back-end. That’s not appropriate for unit testing. We’ll use Jasmine’s AJAX mocking helper, jasmine-ajax. At the time of this writing, the 2.0 branch had not been merged into the main line, and we need the 2.0 branch in order to work with Ext.

cd spec/javascripts/helpers
curl -O 'https://raw.github.com/pivotal/jasmine-ajax/2_0/lib/mock-ajax.js'
git add ./mock-ajax.js

And we’ll create our first store spec in spec/javascripts/store/MyStoreSpec.js:

describe('SenchaBdd.store.MyStore', function () {
  var store;
  beforeEach(function () {
    jasmine.Ajax.useMock();
    clearAjaxRequests();
    store = Ext.create('SenchaBdd.store.MyStore')
  });

  it('calls out to the proper url', function () {
    store.load();
    var request = mostRecentAjaxRequest();
    expect(request.url).toEqual('/colors.json');
  });
});

Notice that I call jasmine.Ajax.useMock() and clearAjaxRequests() in the set up block. This is because I want to wait until the very last moment to turn on ajax mocking. The Ext class loader might still be trying to load a class (via xhr), and the mocker will prevent that from happening. I also clear all previous requests (in case there were any left over, to prevent test polution).

When you run Jasmine, you’ll get a test failure, “TypeError: Cannot read property ‘url’ of null” because we haven’t set up the proxy in the store, yet. Let’s do that.

$ cat app/store/MyStore.js
Ext.define('SenchaBdd.store.MyStore', {
  extend: 'Ext.data.Store',
  config: {
    autoLoad: true,
    storeId:  'mystore',
    fields:   ['color'],
    proxy:    {
      type:       'ajax',
      url:        '/colors.json'
    }
  }
});

Now, when you run the test suite, you’ll get a different error! This is because the default settings for the ajax proxy enables caching, paging, etc. Things we don’t need, so we have to turn them off:

    proxy:   {
      type:       'ajax',
      url:        '/colors.json',
      noCache:    false,
      pageParam:  false,
      startParam: false,
      limitParam: false
    }

Now our api test is green! You can even test this in the application by dropping a file, ‘colors.json’ into the public directory.

Let’s add one more test to finish things off.

  it('populates the collection', function () {
    store.load();
    var mockedRequest = mostRecentAjaxRequest();

    mockedRequest.response({
      status:       200,
      responseText: [
        {color: 'red'},
        {color: 'green'},
        {color: 'blue'}
      ]
    });

    expect(store.getCount()).toEqual(3);
    expect(store.getAt(0).get('color')).toEqual('red');
    expect(store.getAt(1).get('color')).toEqual('green');
    expect(store.getAt(2).get('color')).toEqual('blue');
  });

Every mocked ajax request has a response() method that you can use to inject your own response, synchronously, to your tests. This confirms that MyStore properly parses and arranges the received data so that it can be presented by the view.

Validate your mocks

We now have 2 tests that depend on a back-end to respond in a specified way. How do we keep these tests from drifting out of sync? Since we’ve mocked the store in our view test, we might never know if the back-end API changes!

You can wrap Jasmine expectation in functions so that they are reusable. Then we can mix this matcher into our view test to confirm that the store we use there is the same.

Let’s add this function to our SpecHelper.js file:

function myStoreDataIsValid(store) {
  expect(store.getCount()).toEqual(3);
  expect(store.getAt(0).get('color')).toEqual('red');
  expect(store.getAt(1).get('color')).toEqual('green');
  expect(store.getAt(2).get('color')).toEqual('blue');
}

And we’ll replace our existing tests with a single line:

  myStoreDataIsValid(store);

We’ll also modify the MyViewSpec.js:

--- a/spec/javascripts/view/MyViewSpec.js
+++ b/spec/javascripts/view/MyViewSpec.js
@@ -9,6 +9,7 @@ describe('SenchaBdd.view.MyView', function () {
         {color: 'blue'}
       ]
     });
+    myStoreDataIsValid(store);
     var view = Ext.create('SenchaBdd.view.MyView', {
       renderTo: 'jasmine_content',
       store:    store

Similarly, we can refactor our colors array into var:

--- a/spec/javascripts/helpers/SpecHelper.js
+++ b/spec/javascripts/helpers/SpecHelper.js
@@ -16,6 +16,15 @@ afterEach(function () {
     domEl.setAttribute('style', 'display:none;');
 });
 
+var colorsJSON;
+beforeEach(function() {
+  colorsJSON = [
+    {color: 'red'},
+    {color: 'green'},
+    {color: 'blue'}
+  ];
+});

And then use colorsJSON in our tests.

--- a/spec/javascripts/store/MyStoreSpec.js
+++ b/spec/javascripts/store/MyStoreSpec.js
@@ -18,11 +18,7 @@ describe('SenchaBdd.store.MyStore', function () {
 
     request.response({
       status:       200,
-      responseText: [
-        {color: 'red'},
-        {color: 'green'},
-        {color: 'blue'}
-      ]
+      responseText: colorsJSON
     });
     myStoreDataIsValid(store);
   });

and

--- a/spec/javascripts/view/MyViewSpec.js
+++ b/spec/javascripts/view/MyViewSpec.js
@@ -3,12 +3,9 @@ Ext.require('SenchaBdd.view.MyView');
 describe('SenchaBdd.view.MyView', function () {
   it("has a list of colors", function () {
     var store = Ext.create('SenchaBdd.store.MyStore', {
-      data:     [
-        {color: 'red'},
-        {color: 'green'},
-        {color: 'blue'}
-      ]
+      data:     colorsJSON
     });
+    myStoreDataIsValid(store);
     var view = Ext.create('SenchaBdd.view.MyView', {
       renderTo: 'jasmine_content',
       store:    store

What we’ve managed, so far

The toy app is coming along. We’ve test driven a DataView that consumes a back-end API. We’ve added the mock-ajax library so we can unit tests our stores in isolation. We’ve even seen a few techniques for keeping our mocks from getting out of sync (although, if you’ve been paying attention, I’ve still left a gaping hole, that needs to be plugged).

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Sencha Touch BDD Part 2

Ken Mayer
Friday, April 26, 2013

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: 'test@example.com',
    favoriteColor: 'blue'
  });
  expect(model.get('name')).toEqual('Test');
  expect(model.get('email')).toEqual('test@example.com');
  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.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Sencha Touch BDD Part 1

Ken Mayer
Wednesday, April 17, 2013

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 1 – Getting Started

In this article you will learn how to set up an application to Jasmine tests in your

Opinionated is a good thing

In my not-so-humble professional opinion, every modern web framework should provide a testing infrastructure with each newly generated application. I’m not concerned if it isn’t my preferred testing package. As long as there’s something. Testing is not an option, and the framework authors probably (hopefully?) test, so why not offer a serving suggestion for new projects? The worst that can happen is that you, the developer, disagree with the choice of framework. There’s a little extra bootstrap cost to replace one framework with another. That’s far less expensive than every new developer discovering a way to test.

Sencha Touch 2.1 has a generator built into its sencha command line tool, but it does not create a test structure as part of the template. This article is the first in a series of discoveries about how to test Sencha Touch applications. I am not claiming that this is the one true way to test. This is not necessarily the best way, either. It is, however, something that works. It installs easily on my development laptop. It gets you to your first passing test quickly. It saves you the cost of exploring all of the options and making these discoveries for yourself. You have plenty of other things to worry about.

But first, you need a web server

Once you’ve installed Sencha Touch and Sencha Command (3.1.0.256 when I wrote this), and you’ve generated the template application, you’ll need to serve the pages locally. Most projects will have some sort of app server already running, but it’s not strictly necessary for testing. When I need to serve pages on my own, I prefer pow. It is a zero-configuration server that can host as many applications as you please. I also like the powder ruby gem; it adds a nice command line interface to manage pow. If you are worried about adding a ruby dependency to your project, stop worrying. Sencha Touch uses the compass ruby gem to generate css files; so you already have a ruby dependency.

pow looks for a rack app, but in my sample app, I don’t have one. pow also looks for a directory named public from which it will server static files. The simplest thing that works is to create a symlink named public that points to the root directory of the project.

# Generate a new ST app
$ cd <touch toolkit directory>
$ sencha generate app senchaBdd ~/workspace/sencha-bdd

# Set up pow/powder
$ cd ~/workspace/sencha-bdd
$ ln -s . public
$ powder up
$ powder link

# Test the server
$ open http://sencha-bdd.dev

If all goes well, you should be able to open the application in any web browser at http://sencha-bdd.dev

Running sample app

Running sample app

Install Jasmine

Installing the stand-alone version of Jasmine will work, but it doesn’t scale to hundreds or thousands of specs. That’s why the Jasmine gem was created. I did some more research and found a way to test using the Jasmine gem.

  1. In the root directory of your project add rake and jasmine to your Gemfile
    $ cat Gemfile
    source "https://rubygems.org"
    
    group :development do
    	gem 'rake'
    	gem 'jasmine'
    end
  2. Run bundle install

  3. Run jasmine init

  4. Jasmine will install a basic set up, but there’s some cruft that you won’t need for a Sencha application.
    rm public/javascripts/Player.js
    rm public/javascripts/Song.js
    rm spec/javascripts/PlayerSpec.js
  5. Edit the src_files entry in spec/javascripts/support/jasmine.yml:
    src_files:
        - touch/sencha-touch-all-debug.js   # Load Sencha library
        - spec/app.js                   # Load our spec Ext.Application
        - app/**/*.js                   # Load source files
  6. Create this file in spec/app.js:
    Ext.Loader.setConfig({
        enabled: true,                  // Turn on Ext.Loader
        disableCaching: false           // Turn OFF cache BUSTING
    });
    
    Ext.Loader.setPath({
        'SenchaBdd': 'app'              // Set the path for all SenchaBdd.* classes
    });
    
    Ext.application({
        name: 'SenchaBdd'               // Create (but don't launch) an application
    });
  7. And this one in spec/javascripts/helpers/SpecHelper.js:
    
    Ext.require('Ext.data.Model');
    
    afterEach(function () {
        Ext.data.Model.cache = {};      // Clear any cached models
    });
    
    var domEl;
    beforeEach(function () {            // Reset the div with a new one.
        domEl = document.createElement('div');
        domEl.setAttribute('id', 'jasmine_content');
        var oldEl = document.getElementById('jasmine_content');
        oldEl.parentNode.replaceChild(domEl, oldEl);
    });
    
    afterEach(function () {             // Make the test runner look pretty
        domEl.setAttribute('style', 'display:none;');
    });

So, what’s going on here? Sencha Touch applications need Ext.Loader to manage class loading. You also need an Ext.Application, especially for controller tests. The modifications to jasmine.yml set up the proper load order, and the jasmine gem will find all of the source files underneath the app/ directory. The app.js is a customized version of your normal app.js that sets up the class loader and global namespace configuration. You should replace “SenchaBdd” with the real name of your application. Two things are happening in SpecHelper.js: First, by default Ext.data.Model caches every model created by the application in a global in-memory array. If you don’t clear it between tests, you can be surprised by test pollution. The second part is to set up and clear a space in the test runner for inserting DOM elements, usually for some sort of view testing.

Create a directory structure that matches your application’s

Your application’s directory structure should look something like this:

├── app
│   ├── controller
│   ├── model
│   ├── profile
│   ├── store
│   └── view

Modify the spec directory so that it mirrors the app/ directory:

├── spec
│   ├── app.js
│   └── javascripts
│       ├── model
│       ├── controller
│       ├── view
│       ├── store
│       ├── profile

Install Jasmine

You can get the stand alone version from http://github.com/pivotal/jasmine. Install it at the in the spec directory. I include the version number, so I can experiment with different versions, but that’s a matter of taste.

├── app
│   ├── controller
│   ├── model
│   ├── profile
│   ├── store
│   └── view
├── public -> .
├── resources/
├── spec/
│   ├── controller
│   ├── jasmine-1.3.1
│   ├── model
│   ├── profile
│   ├── store
│   └── view
└── touch

In order to get Jasmine going, you need a special html file named, by convention, SpecRunner. Add it to spec/ as well. It looks like this:

You will need to modify lines 18 and 22 with the name of your ST app (which can be found in app.json and app.js).

Write one passing test

Create a file, spec/javascripts/sanitySpec.js

describe("Sanity", function() {
  it("succeeds", function() {
    expect(true).toEqual(true);
  });
});


Now load the spec runner into a browser. In the case of this sample app, the url is http://sencha-bdd.dev/spec/SpecRunner.html

Now start the Jasmine spec server from the command line:

    bundle exec rake jasmine

And then open a browser window on http://localhost:8888

You should see the test results with one passing spec.

1 Passing Jasmine Spec

1 Passing Jasmine Spec

If you don’t see this, open up the browser’s developer console to look for clues.

Until next time

That’s it! You now have a complete JavaScript testing framework installed in your application. This is a good time to commit your changes. Celebrate in the glory of the green goodness. You’ve earned it.

Next time, I’ll show you how to test a model class.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Robbie Clutton

Thoughts on Simple BDD

Robbie Clutton
Saturday, March 30, 2013

A small number of projects here in New York have adopted my extremely simple behaviour driven development library, SimpleBDD, and I thought I’d share some of the emerging patterns those teams have developed while using it.

SimpleBDD, is a way of using a Gherkin like specification language and turning into a method call with Ruby. It takes the approach of tools like Cucumber but reduces it down the smallest set of features. Essentially taking a method call:

Given "a logged in user"

and calling an underlaying function:

a_logged_in_user

If that method is in the scope of the executing test, that method is executed, if the method isn’t in scope or doesn’t exist the standard method not found exception is raised. It enables a developer to produce Gherkin like specifications while staying in Ruby, using the test framework of choice.

I generally try and follow the advise of my colleague, Matt Parker, with his excellent post on steps as teleportation devises. We try and create a reusable and stateless domain specific language (DSL) for our tests and our steps call into the DSL and hold state pertinent to that particular test run.

At first on my current project we have had three separate areas for our request specs. We have the request spec itself which used SimpleBDD to describe the behavior of the application. We then had a ‘steps’ file which had the methods calls from the SimpleBDD and translated those into the reusable DSL for the application. The steps file was reused across all request specs and was becoming big pretty quickly.

Dirk, on another project which is also using SimpleBDD, skipped the ‘steps’ file and placed those methods straight into the rspec feature files underneath the scenario blocks. Then after some discussions with JT on where to keep the state our tests depended on, Brent and our team started using rspecs ‘let’ methods and the ‘steps’ within the scope of the feature block to keep the intention of the test in one place.

By also putting more responsibility onto the DSL, these methods are pretty dumb, leaving the test describing what the test is attempting to achieve through SimpleBDD method calls and the how through calling the DSL via the ‘step’ methods within the feature block in the same file.


require 'spec_helper'

feature 'homepage' do

  scenario 'happy path' do
    Given "an existing user with one widget"
    When "the user visits the homepage"
    Then "the user can see their widget"
  end

  let(:user) { FactoryGirl.create(:user) }
  let(:widget) { FactoryGirl.create(:widget) }

  def an_existing_user_with_one_widget
    user.widgets << widget # Using the application code to create initial state
    login user # Application DSL
  end

  def the_user_visits_the_homepage
    visit root_path # Capybara DSL
  end

  def the_user_can_see_their_widget
    can_see_widget widget # Application DSL
  end

end

This has been working out fairly well for us and if you're interested in a simple version for BDD for your project I hope you check this out.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Robbie Clutton

Introducing Simple BDD

Robbie Clutton
Saturday, February 2, 2013

Simple BDD is a way to bring structured natural language BDD syntax into any test framework, but why is this necessary?

Have you ever looked back a test you wrote several months ago, or even the several hours ago and wondered what you were thinking and what you were trying to achieve? Behaviour Driven Development (BDD) attempts to resolve this with the structured language or ‘given’, ‘when’ and ‘then’ and natural language describing how an application should behave.

There are a growing number of tools to assist using BDD in Ruby and Rails today, notably Cucumber. Cucumber extracts the behaviour of the application into plain text files using natural language and uses a parser to translate these into methods which should be executed in relation to the natural language.

What if you’ve already got a good test suite, but you just want some of the structured language that something like Cucumber gives you? Well, recently I created a tiny Ruby library to do just this, Simple BDD. Simple BDD allows use of natural language within any executable code, be that RSpec or any flavour of test library.

Given "an authenticated user"
When "that user submits a message less than 140 characters"
Then "that message is displayed to the users followers"
And "is also shown in the users timeline"

These natural language statements are translated into method calls which are executed in the current scope of the test.

an_authenticated_user
that_user_submits_a_message_less_than_140_characters
that_message_is_displayed_to_the_users_followers
is_also_shown_in_the_users_timeline

My colleague Matt Parker recently wrote about how Cucumber steps as teleportation devices and that same thinking applies here. To make the most of this these wordy methods should be considered jumping points into your testing DSLs.

For a more detailed example, check out the Simple BDD example project.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Rasheed Abdul-Aziz

Being Brave is fun.

Rasheed Abdul-Aziz
Saturday, January 19, 2013

You know how tech is. If you don’t stop and look around every, oh, hour or so, a brand new, very interesting technology has surfaced.

If that is not bad enough, old technologies get reinvigorated, like people taking up Vi, Lisp, NeXt… :)

So if you’re like me, are ‘time poor’ and want to get something valuable out of your precious personal-project time, then be brave.

Today I started on a small gem, to help people track what’s taking a lot of time in their Rails Continue reading →

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Robbie Clutton

Testing strategies with RSpec

Robbie Clutton
Tuesday, January 8, 2013

These are some tricks, tips, strategies, lessons learnt from a year of working with RSpec on Rails projects. Rails convention over configuration has proved fruitful for adoption and standardisation but there times when you do want to tweak certain things.  We’ll highlight some ways how that can be done such as changing the directory structure of your tests.

RSpec Directory Structure

Coming from a Java background, I was used to see all of a projects tests in the ‘test’ directory with subdirectories off of those for certain types of tests such as ‘unit’ and ‘integration’.  Using libraries like rspec and rspec-rails sets certain expectations on where certain types of tests will be. Model tests will be in ‘spec/models’, controllers in ‘spec/controllers’. This has been great importing useful helpers for those given situations, but it doesn’t tell me if those are unit tests or something else.

From rspec-rails 2.4 tags such as “type: :controller” can be used to import the appropriate helpers and the tests can be placed where they fit best for the application. I would recommend the following:

spec/
  ./unit
    ./models
    ./controllers
    ./etc...
  ./integration
    ./models
    ./etc...
  ./acceptance

The spec files themselves would look like:

describe FooModel, type: :model do
…
end

RSpec 2.12 (current latest stable) supports tags for model, controller, helper and routing.

Unit testing without a database

Why is it important to do unit testing without hitting a database? If a test hits a database then the execution path is through your business logic, through a persistence library, perhaps a database adapter and then the database. A unit test should be focused on the smallest unit of execution, namely the response and messages sent by a class method. Besides testing this whole stack, there typically tends to be an order of magnitude more unit tests in a project than any other type of test and if the tests are hitting the database hundreds or thousands of times this will eventually lead to a slow test suite.

One way of ensuring unit tests do not hit a database in Rails is to use a null object pattern database adapter. The best known is nulldb.  Although the latest stable release doesn’t support Rails 3.1+ adding the HEAD sha as a dependency will support the very latest Rails projects. The only place an application should really be hitting the database is within the application models so the nulldb adapter can be set for those types of tests.

In Gemfile:

gem 'activerecord-nulldb-adapter', git: 'git://github.com/nulldb/nulldb.git'

In spec/spec_helper.rb:

RSpec.configure do |config|
  ...
  config.before(type: :model) do
    require 'nulldb_rspec'
    ActiveRecord::Base.establish_connection :adapter => :nulldb
  end
  ...
end

If an application is integrating with a software as a service the result is the same. Unit tests should not be hitting that API, stubs should be used to mock the dependency. One way to ensure the tests are not hitting an API would be to open the class that hits the API and overwrite any methods that the application uses and raise an exception.

class ApiClient
  def do_something
    raise “Whoa there, you shouldn’t be here!”
  end
end

If an application does need to sometimes hit that API and sometimes not, RSpec allows models to be included depending on the tag. This would allow some test groups to use the API and others to be intercepted and raise the unexpected visitor error.

config.include ApiInterceptors, group: :model

What’s in an integration test

The integration tests are the best place for testing database integrations like scopes, or complex queries. They are also a good place for testing integrations with other libraries or services such as a message queue, background worker or remote API. An application may also want to test that the layers of an application are integrated correctly. After all with all that stubbing in the unit tests it can mislead a developer as to the correctness of an application. If an application has observers or is event driven this would be a good point to see if those layers are integrated successfully.

Sharing behaviours for integration and acceptance testing

If a project is using the BDD language in stories, the application may be reusing those in its acceptance tests. These stories are often the application codified in human language. Where they are used in the application they are often used in the acceptance testing stage but it doesn’t have to be constrained to there. If those stories are written without specifying the how it can be easier to share, e.g. “a user creates a foo widget” rather than “a user clicks on the ‘create foo widget’; a user fills in ‘bar’ for the foo widget; a user presses the create button’.

RSpec has a concept of ‘shared examples’ which can be used to share behaviours between testing stages. If the test reads in such a way where it could be run against a browser, command line or API, all of those test layers could use the behaviour.

In spec/features/foo_example.rb:

shared_examples "foo" do
  describe "user creates a foo” do
    it "lists new foos" do
      given_user_exists
      when_user_creates_a_foo
      then_foo_is_listed_against_user
    end
  end
end

Then in the target test file, including the shared example will bring that test in and be run in that context with associated tags.

in spec/integration/foo_spec.rb

require 'spec_helper'

def given_user_exists
end

def when_user_creates_a_foo
end

def then_foo_is_listed_against_user
end

describe 'Foo', type: :foo do
  include_examples "a foo"
end

The method calls can be defined so they are inline and in scope, this way each file has its own implementation of the requirement methods for the test. The methods can also be extracted into a module and included through RSpec configuration.

in spec/spec_helper.rb

config.include(IntegrationHelpers, type: :foo)

in spec/support/integration_helpers.rb

module IntegrationHelpers
  def given_user_exists
  end
  def when_user_creates_a_foo
  end
  def then_foo_is_listed_against_user
  end
end

Following this pattern means the business language can be added to code and used in multiple places, and therefore stages of the testing.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Sam Coward

Cedar target templates for Xcode 4.3.x

Sam Coward
Thursday, July 12, 2012

Announcing Cedar target templates for Xcode 4.3.x: Getting started with Cedar has never been easier!

These templates let you quickly add Cedar targets to your iOS or Mac OS X project. It’s super simple to build and install from source now:

git clone http://github.com/pivotal/cedar --recursive && cd cedar ; rake install

If you don’t like building from source, you can also install a build of the Cedar templates from the downloads page – just untar to your home directory.

Using Cedar Templates

Restart Xcode after installing, open your project and add a new target. You’ll find targets for both iOS and Mac OS X. There are 2 types of targets: Suites and Bundles. A Suite target will build your tests as a separate application which you then run to execute your tests. Bundle targets work just like OCUnit testing bundles, providing closer integration with Xcode. If you do create a bundle target, make sure to edit the scheme of your application and add the new target to the list of tests.

Every template comes with a simple example which should run out of the box to help you get running.

ARC: A word of caution

Automatic Reference Counting setting: Don’t use it for your spec targets. Your application is free to use ARC, just don’t use it for any Cedar targets. This is because there is a flaw in the compiler that ships with Xcode (see here for the details, and if you care please feel free to use this information to file a bug with apple).

Have fun speccing

We really hope these changes help you to get started writing specs and spending less time setting up Cedar. If you have any feedback, whether it’s about these templates or something else about Cedar, please feel welcome stop by the discussion group and share your thoughts.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (780)
  • rails (113)
  • testing (88)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (55)
  • techtalk (44)
  • rspec (38)
  • ironblogger (32)
  • productivity (30)
  • activerecord (29)
  • gogaruco (29)
  • git (28)
  • nyc (27)
  • rubymine (26)
  • bloggerdome (23)
  • mobile (22)
  • process (21)
  • pivotal tracker (20)
  • cucumber (20)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • css (13)
  • tdd (13)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to bdd Feed
  1. 1
  2. 2
  3. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >