Unit Testing

We strongly recommend the utilization of Unit Testing practice. In Dynamics Mobile we are fans of Test Driven Development approach, where the unit testing practice is taken further by implementing unit tests first and then satisfying the developed and failing tests by implementing the actual functionality.

Dynamics Mobile Apps development cycle can be augmented further with unit test phase, easily by utilizing any of the publicly available unit test frameworks and compagnian tools, including but not limited to:

You need to read the corresponding configuration for the selected unit testing tool to it setup for your project.

Dynamics Mobile provides a useful test helper classes, which can be used in the unit tests.

class TestHelper

The TestHelper class exposes one useful method createView.

export declare class TestHelper {
static createView(viewClass: any, viewHtml: string, skipDatabind?: boolean): Promise<Object>;
}

The createView method accepts the following arguments:

  • viewClass: View class declared in your project

  • viewHtml: Corresponding html content of the view

  • skipDatabind: optional, defines if the data binding phase must be skipped.

The method creates instance if the given view and executes the "view loading" part of the view lifecycle, which emulates the behavior of the view when displayed on the screen. This process will effectively call the constructor of the view and the load, resume and show methods if available. It will also execute all @input decorators and data-binding and requiring the developer to pass any hard-required input for the via via ContextService or ConfigurationService.

Let's assume we have the following view:

MyView.html
MyView.ts
MyView.html

<div class="page">
<div class="page-content">
<div class="list">
<ul>
<li>
<div data-bind="text: myVar"></div>
</li>
</ul>
</div>
</div>
</div>
MyView.ts

...
@View()
export class MyView {
@input()
public myVar: ko.Observable<number>;
public async load():Promise<void>{
console.log('load called');
}
public async resume():Promise<void>{
console.log('resume called');
}
public getValue(): number {
return this.myVar();
}
}

The view MyView has one observable member called myVar - MyView.ts, line #6. myVar is marked as @input - supposed to be received form the context. The myVar member is also rendered in the UI via databind in MyView.html , line #6.

We can use the following JEST unit test to test our view

import { TestHelper, ContextService } from "@dms";
import * as fs from "fs";
import * as sinon from "sinon";
import { MyView} from "../../src/Views/MyView";
let sandbox: sinon.SinonSandbox;
describe("MyView Task", () => {
//reset sandbox before each test
beforeEach(async () => {
if (!sandbox) sandbox = sinon.createSandbox();
else sandbox.restore();
localStorage.clear();
sessionStorage.clear();
});
//reset sandbox after each test
afterEach(() => {
sandbox.restore();
});
//this is the actual test of the view
it("MyView View", async () => {
//set myVar vairable, which is required by the view
const contextService = RootDIContainer.inject(ContextService);
await contextService.set('myVar', 123);
//load the view's html
const viewHtml = fs.readFileSync(
"./src/Views/MyView.html",
"utf8"
);
//create view instance and execute the view loading sequence
const myViewInstance = (await TestHelper.createView(
MyView,
viewHtml
)) as ChangeTheme;
//call validate method and test the result
const validated = await myViewInstance.validate();
expect(validated).toBeTruthy();
//call commit method and test the result
const commited = await myViewInstance.confirm();
expect(commited).toBeTruthy();
//call other view method and test the result
const value = await myViewInstance.getValue();
expect(commited).toBe(123);
});
});