One of the nice things with new Angular removing reliance on $scope is we can now test controllers just by newing them, we don't need to worry where to get the $scope variable being passed to it. Our controller becomes very POJO, very easy to test.
An example of POJO controller.
///<reference path="../../../typings/requirejs/require.d.ts"/> ///<reference path="../../../typings/angularjs/angular.d.ts"/> ///<reference path="../../../shared/ViewValue/Header.ts"/> ///<reference path="../../../shared/Domain/Product.ts"/> module App.ProductForSale { export class Controller { product : Domain.Product; percentDiscount: number; get discountedPrice() : number { return this.product.price * (1 - this.percentDiscount); } constructor(header: ViewValue.Header) { header.title = "Product for sale"; this.product = new Domain.Product(); } } } define(require => { var mod : angular.IModule = require('theMainModule'); require('/shared/Domain/Product.js'); mod["registerController"]('ProductForSaleController',['singletonHeader', App.ProductForSale.Controller]); });
There's just one minor problem when testing the controller above. It's an AMD controller, to test it we should be able to ignore the define statement in it.
The easiest way to disable it is to monkey-patch the define function by changing it to something else. An example. Name this test setup as _must-be-runned-first.ts:
if (window["def"] == undefined) { window["def"] = window["define"]; window["define"] = function(depArray : string[], c: any) { console.log('define intercepted'); console.log(depArray); }; } function doTest(test) { var def = window["def"]; def(test); }
doTest just wrap the test so tests uses the original define, other defines just got redirected.
Here's a sample test:
///<reference path="../typings/jasmine/jasmine.d.ts"/> ///<reference path="../typings/requirejs/require.d.ts"/> ///<reference path="_must-be-runned-first.ts"/> doTest(require => { describe("Board Controller", () => { require('/base/public/app-dir/ProductForSale/Controller.js'); require('/base/shared/ViewValue/Header.js'); var h = new ViewValue.Header(); it("computes discount", () => { var b = new App.ProductForSale.Controller(h); b.product.price = 50; b.percentDiscount = 0.10; var discountedPrice = 45; expect(b.discountedPrice).toEqual(discountedPrice); }); }); });
By redefining the window["define"] to something else, requirejs won't complain of script error on theMainModule as it is not available when doing tests on controller.
Add test-main.js on files array of karma.conf
module.exports = function(config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '', // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['jasmine', 'requirejs'], // list of files / patterns to load in the browser files: [ 'test-main.js', {pattern: 'shared/**/*.js', included: false}, {pattern: 'public/**/*.js', included: false}, {pattern: 'tests/*.js', included: false} ] }); }
Then on test-main.js, add the _must-be-runned-first.js as the first file to be dynamically-loaded, remove the .js extension when adding it to allTestFiles array.
var allTestFiles = []; var TEST_REGEXP = /(spec|test)\.js$/i; allTestFiles.push('tests/_must-be-runned-first'); // Get a list of all the test files to include Object.keys(window.__karma__.files).forEach(function(file) { if (TEST_REGEXP.test(file)) { // Normalize paths to RequireJS module names. // If you require sub-dependencies of test files to be loaded as-is (requiring file extension) // then do not normalize the paths var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, ''); allTestFiles.push(normalizedTestModule); } }); console.log(allTestFiles); require.config({ // Karma serves files under /base, which is the basePath from your config file baseUrl: '/base', // dynamically load all test files deps: allTestFiles, // we have to kickoff jasmine, as it is asynchronous callback: window.__karma__.start });
Complete Code: https://github.com/MichaelBuen/PlayNodeTypescriptUirouterCouchpotato/tree/master/public
Happy Coding!
No comments:
Post a Comment