{"id":1564,"date":"2016-10-24T15:28:05","date_gmt":"2016-10-24T15:28:05","guid":{"rendered":"http:\/\/blog.soton.ac.uk\/webteam\/?p=1564"},"modified":"2016-10-25T12:03:18","modified_gmt":"2016-10-25T12:03:18","slug":"unit_testing_aurelia_service_code","status":"publish","type":"post","link":"https:\/\/blog.soton.ac.uk\/webteam\/2016\/10\/24\/unit_testing_aurelia_service_code\/","title":{"rendered":"Unit testing Aurelia service code"},"content":{"rendered":"<p>Aurelia is one of a crop of new front end JavaScript frameworks that make it easier to manage complex interactions in the browser. It implements a MVVM pattern and includes routing, dependency injection etc.<\/p>\n<p>Unit testing Aurelia custom elements and attributes is described in the Aurelia docs (<a href=\"http:\/\/aurelia.io\/hub.html#\/doc\/article\/aurelia\/testing\/latest\/testing-components\">http:\/\/aurelia.io\/hub.html#\/doc\/article\/aurelia\/testing\/latest\/testing-components<\/a>. However, more general testing of business logic or service code is not discussed. This article gives a basic introduction to testing these classes using the Aurelia CLI.<\/p>\n<ol>\n<li>What libraries are included in the CLI, what do they do?<\/li>\n<li>Writing a basic test<\/li>\n<li>Running tests<\/li>\n<li>Improving the test output<\/li>\n<li>Debugging tests<\/li>\n<li>Mocking in tests<\/li>\n<li>Testing code with promises.<\/li>\n<\/ol>\n<p>Note: Aurelia supports the babel.js and Typescript transpilers. The code examples below are in Typescript but should be readable for anyone who is familiar with modern javascript.<\/p>\n<h2>1.\u00a0\u00a0\u00a0\u00a0\u00a0 What libraries are included in the CLI, what do they do?<\/h2>\n<p>When creating an Aurelia project using the CLI the following testing libraries are included:<\/p>\n<ul>\n<li>Jasmine \u2013 popular BDD JavaScript testing framework. Provides the test structure and asserts.<\/li>\n<li>Karma \u2013 test runner. Allows you to run the tests from the command line and debug the tests within a browser.<\/li>\n<\/ul>\n<p>Angular Protractor and selenium web-driver tests are also included \u2013 they are used for end to end testing and so not discussed in this article.<\/p>\n<h2>2.\u00a0\u00a0\u00a0\u00a0\u00a0 Writing a basic test<\/h2>\n<p>Test classes are placed in the \/tests folder and should include spec in the title e.g. articleStore.spec.js. The test class should include the following elements.<\/p>\n<h3>Import referenced classes<\/h3>\n<p>Import the class under test (and any other relevant classes) at the top of the test class e.g.<\/p>\n<pre>Import { ArticleStore } from \u2018..\/..\/src\/articles\/ArticleStore\u2019;<\/pre>\n<h3>Create a top level describe function.<\/h3>\n<p>Create a describe function that indicates the name of the class under test as a string and the details of the tests as an argument e.g.<\/p>\n<pre>describe(\u2018the ArticleStore\u2019, () =&gt; {\u2026});<\/pre>\n<p>Note: The text \u2018the ArticleStore\u2019 will then be outputted when we run the tests.<\/p>\n<h3>Create a test setup<\/h3>\n<p>As with most testing framework Jasmine provides a mechanism to run setup and teardown code before\/after each individual test. This is achieved by creating a beforeEach\/afterEach function that takes a function as an argument.<\/p>\n<p>In this example we will use this to create an instance of the class under test before each test is run e.g.<\/p>\n<pre>let target: ArticleStore;\r\n\r\nbeforeEach(() =&gt; {\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 this.target = new ArticleStore();\r\n\r\n});<\/pre>\n<h3>Create a test<\/h3>\n<p>To create the test itself we create an it() function which takes the name of the test as a string and the test code as an function argument. Again, the name of the test will be outputted by the test runner.<\/p>\n<p>The test itself makes use of the Jasmine asserts to confirm expected state.<\/p>\n<p>e.g.<\/p>\n<pre>it(\u201cshould have an empty articles collection\u201d, () = {\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 expect(this.target.Articles).not.toBeNull();\r\n\r\n      expect(this.target.Articles.length).toBe(0);\r\n\r\n});<\/pre>\n<h2>3.\u00a0\u00a0\u00a0\u00a0\u00a0 Running tests<\/h2>\n<p>To run the tests issue the following statement from a command prompt:<\/p>\n<pre>au test<\/pre>\n<p>This invokes the Karma test runner to run any tests it finds in files ending spec.js and outputs the details of any tests that have failed.<\/p>\n<p>\u201cau test\u201d will run the tests once, report the output and close. However, as with the \u201cAurelia run\u201d command you can include the watch argument:<\/p>\n<pre>au test --watch<\/pre>\n<p>This will run the tests, report the output but not close. The test runner will maintain a watch for any changes to code and when new code is saved will rerun the tests and display the new output.<\/p>\n<h2>4.\u00a0\u00a0\u00a0\u00a0\u00a0 Improving the test output<\/h2>\n<p>The default configuration of Karma within Aurelia will only report failing tests. To get a comprehensive list of all tests that were run make the following change to the \/karma.conf.js file:<\/p>\n<p>Change this: \u00a0\u00a0\u00a0\u00a0 reporters: [&#8216;progress&#8217;],<\/p>\n<p>To this:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 reporters: [&#8216;spec&#8217;],<\/p>\n<h2>5.\u00a0\u00a0\u00a0\u00a0\u00a0 Debugging tests<\/h2>\n<p>Karma makes debugging quite easy as it creates a browser instance and allows you to use the standard in browser debug tools (F12). As well as being simple to use, debugging in the browser is more accurate than debugging in an IDE or similar, as it will correctly replicate any browser issues.<\/p>\n<p>To debug from the Karma test runner:<\/p>\n<ul>\n<li>In the command prompt run: au test \u2013watch<\/li>\n<li>A browser spins up. Click the \u2018Debug\u2019 button in the green bar on the top right.<\/li>\n<li>A new tab opens with the unit tests loaded. Press F12 to open developer tools, view the source, add breakpoints etc. as normal and press refresh to re-run the tests and hit the breakpoints.<\/li>\n<\/ul>\n<h2>6.\u00a0\u00a0\u00a0\u00a0\u00a0 Mocking in Aurelia tests<\/h2>\n<p>Mocking is an important part of any unit testing strategy. Currently the standard tool for mocks\/stubs\/spies in Javascipt is to use the sinon.js library. However in my experience this did not play well with Jasmine.<\/p>\n<p>An alternative, simpler and more modern mocking library is called TestDouble (https:\/\/github.com\/testdouble\/testdouble.js) . This can be imported via NPM.<\/p>\n<h3>Import the testdouble package<\/h3>\n<p>npm install testdouble \u2013save-dev<\/p>\n<h3>Add testdouble as a vendor-bundle dependency.<\/h3>\n<p>This is optional but adding the following to the dependencies section of the \/aurelia-project\/aurelia.json file, vendor-bundle dependencies section makes regularly including the testdouble library within test classes simpler as you can refer to it with a simple name rather than needing the relative path.<\/p>\n<pre>{\r\n        \"name\": \"testdouble\",\r\n        \"path\": \"..\/node_modules\/testdouble\/dist\/testdouble\"\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h3>Import TestDouble in your test class<\/h3>\n<p>Import * as TestDouble from \u2018testdouble\u2019;<\/p>\n<p>Note include the relative path if you did not complete the previous step<\/p>\n<p>&nbsp;<\/p>\n<h3>Create\/Destroy the mock objects<\/h3>\n<p>Within the test code create a variable for the object being mocked, with the type of the object being mocked e.g. if we are mocking a class of type ApiConnector<\/p>\n<pre>let apiConnector: ApiConnector;<\/pre>\n<p>Initialize the mock on the beforeEach() method, and call TestDouble.reset() on the afterEach() method e.g.<\/p>\n<pre>beforeEach(() =&gt; {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 this.apiConnector = TestDouble.object(ApiConnector);\r\n});<\/pre>\n<pre>afterEach(() =&gt; {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 TestDouble.reset();\r\n});<\/pre>\n<p>&nbsp;<\/p>\n<h3>Setup and\/or verify the mocks within the tests<\/h3>\n<pre>It(\u2018getStatus should return the updating status from the apiConnector\u201d, () =&gt; {\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ setup the mock\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 let knownStatus = \u201ccached\u201d;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 TestDouble.when(this.apiConnector.getStatus).thenReturn(knownStatus);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ make the call under test\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 Let result = this.articleStore.getStatus();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n      \/\/ assert the mocked result is returned and the call was made on the mock object\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 Expect(result).toBe(knownStatus);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 this.articleStore.verify(getApiStatus);\r\n});<\/pre>\n<p>&nbsp;<\/p>\n<h2>7.\u00a0\u00a0\u00a0\u00a0\u00a0 Testing code containing promises<\/h2>\n<p>Asynchronous code is very common in the JavaScript world and a modern approach to implementing async code is to use promises. Asynchronous code and promises requires some minor changes in testing approach.<\/p>\n<p>To handle async code Jasmine takes an optional argument to the it(), beforeEach() and afterEach() methods called \u201cdone\u201d. The \u201cdone\u201d argument is a method that should be called once all other test code has completed. When using a promises approach to asynchronous code this would typically be at the end of the last \u201cthen\u201d call.<\/p>\n<p>The following code gives an example of creating a promise object and returning this promise from a mocked method. This is a common scenario e.g. where an ajax call which returns a promise needs to be mocked.<\/p>\n<p>For a promise to be processed either its resolve or reject method should be called. In this example the mock returns a promise that has a resolve method and the test asserts that when that promise is successfully resolved the refreshAll method returns a promise with the data true.<\/p>\n<p>&nbsp;<\/p>\n<pre>it(\"refreshAll returns a promise with data:true when api call successful\", (done) =&gt;\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ arrange\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 let promise = new Promise((resolve, reject) =&gt; { resolve(\"Success data\"); });\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TestDouble.when(this.apiConnector.getMany()).thenReturn(promise);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ act\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 let result = this.articleStore.refreshAll();\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ assert\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 result.then(data =&gt; {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 expect(data).toBe(true);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 done();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 });\u00a0\r\n\r\n\u00a0\u00a0\u00a0 });<\/pre>\n<p>&nbsp;<\/p>\n<h1>Complete example code<\/h1>\n<p>As a summary, I have included below a complete example of an Aurelia test class which mocks promises:<\/p>\n<pre>import { ArticleStore } from '..\/..\/..\/..\/src\/resources\/data-service\/ArticleStore';\r\n\r\nimport { ApiConnector } from '..\/..\/..\/..\/src\/resources\/data-service\/ApiConnector';\r\n\r\nimport * as TestDouble from 'testdouble';\r\n\r\ndescribe('the ArticleStore', () =&gt; {\r\n\r\n\u00a0\u00a0\u00a0 \/\/ setup\r\n\r\n\u00a0\u00a0\u00a0 let apiConnector: ApiConnector;\r\n\r\n\u00a0\u00a0\u00a0 let articleStore: ArticleStore;\r\n\r\n\u00a0\u00a0\u00a0 beforeEach(() =&gt; {\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TestDouble.reset();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 this.apiConnector = TestDouble.object(ApiConnector);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 this.articleStore = new ArticleStore(this.apiConnector);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n\r\n\u00a0\u00a0\u00a0 });\r\n\r\n\r\n\r\n\r\n\u00a0\u00a0\u00a0 afterEach(() =&gt; {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TestDouble.reset();\r\n\u00a0\u00a0\u00a0 });\r\n\r\n\r\n\r\n\r\n\u00a0\u00a0\u00a0 it(\"refreshAll returns a promise with data:true when api call successful\", (done) =&gt;\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ arrange\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 let promise = new Promise((resolve, reject) =&gt; { resolve(\"Success data\"); });\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TestDouble.when(this.apiConnector.getMany()).thenReturn(promise);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ act\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 let result = this.articleStore.refreshAll();\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ assert\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 result.then(data =&gt; {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 expect(data).toBe(true);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 done();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 });\u00a0\r\n\r\n\u00a0\u00a0\u00a0 });\r\n\r\n});<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Aurelia is one of a crop of new front end JavaScript frameworks that make it easier to manage complex interactions in the browser. It implements a MVVM pattern and includes routing, dependency injection etc. Unit testing Aurelia custom elements and attributes is described in the Aurelia docs (http:\/\/aurelia.io\/hub.html#\/doc\/article\/aurelia\/testing\/latest\/testing-components. However, more general testing of business logic [&hellip;]<\/p>\n","protected":false},"author":97681,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1564","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/posts\/1564","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/users\/97681"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/comments?post=1564"}],"version-history":[{"count":4,"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/posts\/1564\/revisions"}],"predecessor-version":[{"id":1579,"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/posts\/1564\/revisions\/1579"}],"wp:attachment":[{"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/media?parent=1564"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/categories?post=1564"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.soton.ac.uk\/webteam\/wp-json\/wp\/v2\/tags?post=1564"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}