Chapter 6. Testing, Building, and Deploying

At this point we've talked about what SproutCore is, built a simple application, and been into great detail on many of the individual features of the framework, but we haven't yet completed the whole picture. In this chapter we will flesh out some of the remaining pieces so that you're ready to build a real SproutCore application on your own from start to finish.

First, we will look at how to prove out the functionality of your objects and methods using unit tests, which we will see are simple to add to your app. Next, we will look at several options used for configuring the build tools, including proxying requests to avoid same-origin policy restrictions while developing, embedding external JavaScript files and several other common scenarios.

Finally, we will run through the process of building an application in order to deploy it on a web server to make it available to the world. Every deployment is unique, but once we understand exactly what a built SproutCore app is, it will be trivial for you to deploy it on whatever platform you are using.

In this chapter we will cover the following:

  • Unit testing SproutCore apps
    • Viewing unit test results
  • Using frameworks and modules
  • Building and deploying apps
    • Additional configuration options

Unit testing SproutCore apps

I might as well admit it; I am a big fan of unit tests. Once I discovered that unit testing is really a code development tool for improving quality, I've been pretty much sold on them. But first let me explain why I believe unit tests are the best tool for improving the quality of code. Take this simple helper method of a view for example:

// Returns the display name of the content
displayName: function (content) {
  if (MyApp.isDownloading  && !this.isReady) {
    return "Downloading…";
  } else {
    return "Name: " + content.name;
  }
}

As short as this function is, it still manages to contain a couple of problems that would be highlighted by adding a unit test.

For an instance, it wouldn't take much time to spot problems when writing a unit test that asked "what happens if content is null?" and "what if content.name is undefined?". It also wouldn't take long while trying to test this method to discover that the dependency on external variables, MyApp.isDownloading and this.isReady, makes the method difficult to test. If the component of code can't be isolated and tested as a unit, it is a clear sign of a poorly written component. Obviously, for such a simple method, we can see the problems without needing to test first, but it's easy to miss even simple mistakes when working under stress and deadlines. Plus, we never know who may come in later to tweak a method and unknowingly break an assumption that we had placed in the code. That's why unit tests are also wonderful tools for preventing regressions.

So let's look at unit testing in SproutCore. SproutCore contains the QUnit unit-testing framework that is used by jQuery (http://qunitjs.com). If you're familiar with QUnit, you'll already know how to write your unit tests in SproutCore.

Unit tests are contained in the tests directory of an app or framework. If we look at the Contacts app we made in Chapter 1, Introducing SproutCore, we will see that by using the command line generators to create our controller and model files, we also got several test stub files as well. Let's revisit the Contacts app and add a few real unit tests.

First let's test the Contact model in tests/models/contact_test.js. If you open up the stub file that was generated, it should look something like the following screenshot:

Unit testing SproutCore apps

The first function, module(), is used for starting a new group of tests. In this case, the group of tests is called "Contacts.Contact". Inside of this module, we see one test() function that is passed a description for the test and the function to run. Before we modify this file with actual tests, let's see how we actually run and view the results of the tests.

Viewing unit test results

SproutCore includes an app just for unit testing called Test Runner that is available at http://localhost:4020/sproutcore/tests. If you simply launch http://localhost:4020, you will see it in the list of available apps as "tests". When you do launch the unit-testing app, you will first see all of the apps, frameworks, modules, and themes in the current project listed along with the SproutCore framework that itself includes several apps, sub-frameworks, and themes that can be tested. For example, take a look at the following screenshot:

Viewing unit test results

Note

Although you can run the SproutCore unit tests, they are for developers that are working on code within SproutCore to contribute back to the project and so we don't normally care about running them.

Right now, we're only going to focus on tests within the Apps section, where we see our contacts app is listed. The framework my_framework and the module my_module are just there for display purposes and don't actually contain any code. I have added them so that I could show how we can test apps, frameworks, and modules all through the single Test Runner app. We'll look at creating frameworks and modules later on in this chapter.

To run a suite of tests, we simply select the matching test file from the list. Running our Contacts.Contact model test, models/contact_test, gives us the following result:

Viewing unit test results

So now it's just a matter of adding as many tests as we can imagine and hitting the Reload button to verify the results of the additional tests. Let's try out a couple of QUnits test functions in the Contacts.Contact test.

First, we could test the output of the fullName computed property to look for problems. Let's do this by replacing the default test with one specific to the fullName property, like the following code:

test("Test the fullName property: basic tests", function () {
  var store = SC.Store.create(),
    contact;

  // No firstName or lastName.
  contact = store.createRecord(Contacts.Contact, {});
  equals(contact.get('fullName'), '', "`fullName` with no firstName & lastName should equal");

  // firstName only
  contact = store.createRecord(Contacts.Contact, { firstName: 'Julius' });
  equals(contact.get('fullName'), 'Julius', "`fullName` with firstName & no lastName should equal");

  // lastName only
  contact = store.createRecord(Contacts.Contact, { lastName: 'Caesar' });
  equals(contact.get('fullName'), 'Caesar', "`fullName` with lastName & no firstName should equal");

  // firstName & lastName
  contact = store.createRecord(Contacts.Contact, { firstName: 'Julius', lastName: 'Caesar' });
  equals(contact.get('fullName'), 'Julius Caesar', "`fullName` with firstName & lastName should equal");

  // Clean up.
  store.destroy();
});

Tip

How you organize your modules and tests is up to you. For debugging purposes, I find it's easier to have a lot of smaller tests with only a few assertions than it is to have large tests with many assertions.

Reloading that test in the browser shows us that all our basic assumptions were correct and all four conditions passed for the test as shown in the following screenshot:

Viewing unit test results

Now for all of our future tests, we will always need to create and destroy a store each time, which would be wasteful. Instead, we can use the setup and teardown options of the module to set up and clean up the common code that all the tests in the module can share.

For example, we could write our Contacts.Contact module so that every test has the store object available, shown as follows:

var store;
module("Contacts.Contact", {

  setup: function () {
    store = SC.Store.create().from(SC.Record.fixtures);
  },

  teardown: function () {
    store.destroy();
    store = null;
  }

});

The final thing you will notice is that I have added the fixtures data source to the store that I have created in the example. When testing models, this makes it really easy to test them thoroughly using the fixture data that you are developing against. As your fixture data grows and becomes more complex, your unit tests can leverage this work to test out more complex scenarios.

For now, here's a simple example showcasing using the fixture data to test a model:

test("Test the fullName property: fixtures", function () {
  var contact;

  contact = store.find(Contacts.Contact, 'tyler'),
  equals(contact.get('fullName'), 'Tyler Keating', "`fullName` for 'tyler' should equal");
});

Now that we've seen how to basically write and run unit tests, we won't dwell on the specifics any further. There are actually over 3,250 unit tests in the SproutCore framework with over 21,300 assertions and so you may want to visit the source on Github to look for examples. That along with the QUnit documentation will help you further improve your unit testing ability.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.119.81