Verifier worked example

Using Verifier to Write Acceptance Tests for a Simple Trade Tile

The Verifier framework simplifies the creation and execution of tests for complex components such as trade tiles. In the Presenter Worked Example we created a simple trade tile using the Presenter library. Any Presenter component, such as the tile, is composed of a presentation model which consists of fields and properties and an HTML view the elements of which are bound to the properties in the presentation model.

In this tutorial we’re going to create an acceptance test for that trade tile, to check that the view-model bindings between the Presenter component model, and the template it is using are correct. If you have not worked through it yet, you may want to familiarise yourself with what we did in the Presenter worked example before you embark on this one.

These tests could then be integrated with a Continuous Integration build environment (Jenkins, Go, or similar), to provide automated regression testing as part of the build process for a project.

As was the case for the other worked example, we’re using an application called TutorialMotif with the namespace tutorial and a blade called simpletile. In the tutorial for Presenter we created the tile in a bladeset called presenter. the purposes of this example we will make a separate bladeset, although in practice tests will live inside the working blades. So, first launch the BladeRunnerJS dashboard for TutorialMotif and create a new bladeset called verifier so that you can build on it while leaving the Presenter tutorial untouched.

Then simply copy the simpletile blade from the blades directory in the presenter-bladeset to the matching directory in the verifier-bladeset.

Remember to change the namespace tutorial.presenter.simpletile to tutorial.verifier.simpletile throughout, and also the src\tutorial\presenter folder name in the src and workbench.

Part 1: Set-up the test infrastructure

The directory structure for our blade should be similar to that shown below. Create the acceptance test directory structure (starting with test-acceptance) as indicated. The bundles directory will be populated with the bundles generated when the tests are run. We’ll create our acceptance test at the lowest level of the tests directory.

verf we 1

Next you’ll create and populate the jsTestDriver.conf file in the js-test-driver directory as follows:

verifier-bladeset\blades\simpletile\tests\test-acceptance\js-test-driver\jsTestDriver.conf
server: http://localhost:4224
basepath: .
plugin:
    - name: "BundleInjector"
      jar: ../../../../../../../../sdk/libs/java/testRunner/js-test-driver-bundler-plugin.jar
      module: com.caplin.jstestdriver.plugin.CutlassBundleInjectorPlugin
load:
    - bundles/js/js.bundle
    - bundles/i18n/en_i18n.bundle
serve:
    - bundles/html.bundle
    - bundles/xml.bundle
test:
    - tests/**.js

This configuration file is very sensitive when it comes to spacing, for this file to be parsed correctly ensure each line that begins with a dash is preceded by two spaces only (no tabbing).

Part 2: Writing the acceptance test

We’re going to write an acceptance test which opens the tile and sets the buy and sell property values on the tile’s presentation model. What we expect when this is done, is that the tile’s view (HTML representation) will reflect these values, rounded to three decimal places, and that they will show up in the right places. Create a test file called SimpleTileTest.js and populate it with the following code:

verifier-bladeset\blades\simpletile\tests\test-acceptance\tests\tutorial\verifier\simpletile\SimpleTileTest.js
caplin.testing.GwtTestRunner.initialize();

caplin.include("caplin.presenter.component.PresenterComponentFactory");

describe("Simple Tile binds correctly", function(){
  fixtures("tutorial.verifier.simpletile.testing.SimpleTileTradeFixtureFactory");

  it("correctly binds and rounds the buyRate", function(){
    given("tile.viewOpened = true");
      and("tile.model.setupNode.buyRate = 1.1234567");
    then("tile.view.(div button:first).text = '1.123'");
  });

  it("correctly binds and rounds the sellRate", function(){
    given("tile.viewOpened = true");
      and("tile.model.setupNode.sellRate = 0.987654");
    then("tile.view.(div button:eq(1)).text = '0.988'");
  });
});

Because the syntax is quite user-friendly and "human readable", it’s fairly easy to convey the objective of each test. Before the test will run however, there are some things that need to be done:

  • Initialize the GwtTestRunner, to start the Given-When-Then adapted version of Jasmine that Verifier uses to run tests. The first line of the above code handles this.

  • Include the PresenterComponentFactory so that a Presenter based component can be used in the test. This is handled by the caplin.include line in the code.

  • Include the component’s FixtureFactory which will create instances of the fixtures that the test will require to be able to run. This is included within the fixtures() class, as shown above, but there is a bit more to it than that…​

The SimpleTileTradeFixtureFactory is what associates the tile. property reference to the tile component via a fixture. You’ll create it in the next step of the tutorial, before you run the test.

The first line of each test will begin with a given, which, unless it continues from another test, will use a fixture to create the Presenter component and its view. The second clause, when, then accesses the model of the component and sets the buy/sellRate property value. In this case, the then clause accesses the view to check the resulting value. It uses a jQuery selector to locate the DOM element where the set values should appear.

Although the example above makes use of eq(1) to locate a button, it is advisable to use class names and IDs where possible to make sure the tests are resistant to changes in the html.

Part 3: Setting the alias scenario

The code we are testing makes use of services, but we will not want our test to use the real implementations of those services. To use stub implementations of these services, we simply have to change the alias scenario to "dev". This can be done by adding an aliases.xml file to the js-test-driver\resources directory as follows:

verifier-bladeset\blades\simpletile\tests\test-acceptance\js-test-driver\resources\aliases.xml
<aliases useScenario="dev" xmlns="http://schema.caplin.com/CaplinTrader/aliases">
</aliases>

Part 4: Creating the FixtureFactory

The SimpleTileTradeFixtureFactory needs to create any fixtures which will be required by the test. These may be generic fixtures, or possibly a bespoke fixture will be required. The tests we’re performing are view-model binding tests, so the generic fixtures should be sufficient. In particular we need to register the PresenterComponentFixture that will be used, passing it the appropriate view template and presentation model that the tile uses. The fixture factory’s contents should be as below.

verifier-bladeset\blades\simpletile\src\tutorial\verifier\simpletile\testing\SimpleTileTradeFixtureFactory.js
/**
* Constructs the SimpleTileTradeFixtureFactory required to run tile tests.
*/
tutorial.verifier.simpletile.testing.SimpleTileTradeFixtureFactory = function() {
  //Set date format as to match that expected by the tests
  caplin.i18n.Translator.getTranslator().getDateFormat = function() { return "DD MMM YYYY";};
};

caplin.implement(tutorial.verifier.simpletile.testing.SimpleTileTradeFixtureFactory, caplin.testing.FixtureFactory);

/**
* This method is called by the test-runner before each Jasmine tests.
*/
tutorial.verifier.simpletile.testing.SimpleTileTradeFixtureFactory.prototype.setUp = function()
{
  var oConnectionServiceStub = caplin.core.ServiceRegistry.getService("caplin.connection-service");
  this.m_oConnectionServiceFixture.setConnectionServiceStub(oConnectionServiceStub);
};

/**
* This method is called once by the test-runner after the SimpleTileTradeFixtureFactory is constructed and adds
* to the test runner all the fixtures that are needed by the tests, assigning to each a corresponding node expression.
* The fixtures required by the tests are for the tile presenter component (tile.model., tile.view.)
*/
tutorial.verifier.simpletile.testing.SimpleTileTradeFixtureFactory.prototype.addFixtures = function(oTestRunner) {
  var fOnOpenCallback = function(oComponent) {};
  this.m_oComponentFixture = new caplin.presenter.testing.PresenterComponentFixture("tutorial.verifier.simpletile.simpletile-template", "tutorial.verifier.simpletile.TilePresentationModel");
  this.m_oComponentFixture.onOpen(fOnOpenCallback);
  oTestRunner.addFixture("tile", this.m_oComponentFixture); //tile.*

  // - Connection Service Fixture -
  this.m_oConnectionServiceFixture = new caplin.services.testing.ConnectionServiceFixture();
  oTestRunner.addFixture("connection", this.m_oConnectionServiceFixture); // connection.*
};

Part 5: Running the tests

Before running the tests, we will need a browser to serve them. First ensure that test-runner.conf, found in BladeRunnerJS’s conf directory, has at least one reference to a browser. For example, under browserPaths: we should see something like chrome: C:\Program Files\Google\Chrome\Application\chrome.exe

For the purposes of this example, to view how the results look when we have failing tests, remove the DecimalFormatter.js file along with the references to the formatter in SetupPresentationNode.js (instructions on adding this back in will follow).

You should now be ready to run the tests, by starting the server and running the appropriate test as described in the Verifier overview.

  • In a command prompt window, navigate to the BladeRunnerJS sdk directory.

  • Type brjs test-server. If successful this will open a browser window and say Server: Waiting.

  • In a separate command prompt window, navigate also to the sdk directory.

  • Type brjs test ..\apps\TutorialMotif\verifier-bladeset\blades\simpletile ATs (i.e. brjs test <dir> <testType>). Be careful not to point at a directory too low in the hierarchy as Verifier is expecting to find a jsTestDriver.conf file and a tests directory below the \simpletile\tests\test-acceptance\js-test-driver directory.

Part 6: Temporarily removing tests / test suites from the test run

While modifying or adding individual tests it is sometimes convenient to temporarily remove tests that don’t concern us so we can repeatedly run our tests as quickly as possible. To remove an entire suite of tests from the test-run, we simply replace describe with xdescribe. Likewise, to stop an individual test from running within a suite of tests, we replace it with xit:

describe("Simple Tile binds correctly", function(){
  fixtures("tutorial.verifier.simpletile.testing.SimpleTileTradeFixtureFactory");

  xit("correctly binds and rounds the buyRate", function(){
    given("tile.viewOpened = true");
      and("tile.model.setupNode.buyRate = 1.1234567");
    then("tile.view.(div button:first).text = '1.123'");
  });

  it("correctly binds the buy button", function(){
    given("test.continuesFrom = 'correctly binds and rounds the buyRate'");
    
  });

});

Using this method will enable us to run a smaller sample of tests, while still being able to continue from the tests that we have temporarily removed. For example, in the test above, the first test is not run, however we can still run the following test which continues from it. Another benefit of using this approach is that it makes it much easier to find removed tests in the code, the test results will also reference the tests that are not running.

Part 7: Possible pitfalls

While you are creating your tests, watch out for the following common pitfalls and omissions:-

Browser configuration

You may receive an error while running the brjs test or test-server commands, if you do not have at least the Chrome browser correctly configured in your test-runner.conf file. Find this configuration file in your {bladerunnerjs_root}/conf directory, and ensure that the path to the browser’s exe file is correct for your machine in the browserPaths: section of this file.

Incorrect test infrastructure

You may receive an error if the XML bundler is not included, or you do not have the appropriate initialisation code in your jsTestDriver.conf file. For example if you only have…​

serve:
    - bundles/html.bundle

then add the following line to your jsTestDriver.conf file:

- bundles/xml.bundle

This will then automatically bundle all XML in your application, and make it available within your code.

In addition, you need to ensure that each test file includes the first line:

caplin.testing.GwtTestRunner.initialize();

Part 8: Test results

If you’ve already applied a formatter to the buyRate and sellRate in the SetupPresentationModel, then the tests should pass and you will see the following result output:

C:\BladeRunnerJS\sdk>brjs test ..\apps\TutorialMotif\verifier-bladeset\blades\simpletile ATs
BladeRunnerJS SDK version: 3.0.0-241935, built on: 17 October 2012 16:53 BST Server already running, not bothering to start a new instance...

Testing tests (ATs):
Chrome: Reset
Chrome: Reset
..
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (41.00 ms)
  Chrome 22.0.1229.94 Windows: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (41.0
0 ms)
19-Oct-2012 11:03:23 com.google.jstestdriver.ActionRunner runActions
INFO:
Tests Passed.

- Time Taken: 5secs

If you have not applied a formatter, the tests will fail. In order to implement the expected behaviour and get the tests to pass, you’ll need to create a formatter to round the values to three decimal places and apply it to the buyRate and sellRate properties.

Create the formatter:

verifier-bladeset\blades\simpletile\src\tutorial\verifier\simpletile\formatter\DecimalFormatter.js
/**
* Formatter that formats the value to the specified number of decimal places.
*/
tutorial.verifier.simpletile.formatter.DecimalFormatter = function() {
};

caplin.implement(tutorial.verifier.simpletile.formatter.DecimalFormatter, caplin.core.Formatter);

/**
* Format the value to the specified number of decimal places.
*/
tutorial.verifier.simpletile.formatter.DecimalFormatter.prototype.format = function(vValue, mAttributes) {
  return caplin.core.Number.isNumber(vValue) ? caplin.core.Number.toFixed(vValue, mAttributes["dp"]) : vValue;
};

tutorial.verifier.simpletile.formatter.DecimalFormatter.prototype.toString = function() {
  return "tutorial.verifier.simpletile.formatter.DecimalFormatter";
};

tutorial.verifier.simpletile.formatter.DecimalFormatter = new tutorial.verifier.simpletile.formatter.DecimalFormatter();

Next, apply the formatters to the properties:

verifier-bladeset\blades\simpletile\src\tutorial\verifier\simpletile\SetupPresentationNode.js
tutorial.verifier.simpletile.SetupPresentationNode = function(oParentPresentationNode) {
  
  this.buyRate.addFormatter(tutorial.verifier.simpletile.formatter.DecimalFormatter, {dp:3});
  this.sellRate.addFormatter(tutorial.verifier.simpletile.formatter.DecimalFormatter, {dp:3});
  
};

When you now run the tests, they should discover the expected values and pass.