Skip to content

Testing: End to End Application Testing

Jonathan Niles edited this page May 6, 2016 · 2 revisions

End to End Testing

End to end testing allows developers to know if changes they made have affected the overall functioning of the application. These tests are designed to test how controllers, services and components all fit together to accomplish a single goal, such as login or billing. bhima uses protractor to run the end to end test suite.

Running End To End Tests

To properly run end to end tests, you must do three things:

  1. Rebuild the test database from scratch
  2. Build and start the server
  3. Run protractor

If your development environment is Unix-based, there is a shell script provided to automate the testing procedure.

# drop and build the db, run the server, fire up protractor 
./sh/test-ends.sh

On non-unix based environments, you will have to do this manually or write your own scripts. If you do, please contribute them back!

End to end tests ought to span the entire functionality of the application and so may take a while. As a best practice, you should run end to end tests before you make a pull request.

Writing End To End Tests

All tests in bhima require the suffix .spec.js to identify them as tests and not library functions.

In bhima, there are tools to help you write end to end tests. These tools are found in client/test/e2e/shared. Their goal is to enable the developer to write precise tests without worrying about the details of the environment, low level style and DOM manipulation, angular quirks, etc. They abstract away from the underlying base to provide high-level access to the view and controller.

One particularly useful library is FormUtils.js. It should be imported like this:

var FU = require('../shared/FormUtils');

For full usage, please see the source code and generated documentation. However, a brief reference overview is given below:

Interacting with <input>s
// clears the input and calls .sendKeys('Some new Value') on the <input ng-model="Controller.model.value"> element
FU.input('Controller.model.value', 'Some new Value');

// checks the second radio button with ng-model="Controller.model.radioValue"
FU.radio('Controller.model.radioValue', 2);
Validation States
// assert that the <input ng-model="Controller.model.value"> is valid
FU.validation.ok('Controller.model.value');

// assert that the <input ng-model="Controller.model.value"> is invalid
FU.validation.error('Controller.model.value');

// assert that an element (identified by a locator) will eventually be added or be removed 
// signature: FU.exists(locator, boolean);
// some examples follow:

// assert that an element data-error-message appears
FU.exists(by.css('[data-error-message]'), true);

// assert that an element with id="enterprise-2" disappears
FU.exists(by.id('enterprise-id'), false);

// assert that an <input ng-model="Controller.model.value"> disappears
FU.exists(by.model('Controller.model.value'), false);
Buttons

If a developer constructs their view with certain data- attributes, the FormUtils library will be able to use these to easily invoke actions.

/** if there is a <button data-method="create"  ...> in the view, it will be clicked */
FU.buttons.create();

/** if there is a <button data-method="submit"  ...> in the view, it will be clicked */
FU.buttons.submit();

/** if there is a <button data-method="cancel"  ...> in the view, it will be clicked */
FU.buttons.cancel();

/** if there is a <button data-method="delete"  ...> in the view, it will be clicked */
FU.buttons.delete();

These are particularly useful if the module has many forms. If all buttons are tagged with data-* attributes, the tests can easily hook into the form and perform complex actions.

What should be tested?

It is often difficult to decide what to test. A good rule is this: any operation that a user can do within the application should be covered by an end to end test.