AngularJS TypeScript单元测试

Mic*_*yna 7 javascript mocha.js angularjs typescript tsc

我很难为angularjs(v1.4.9)应用程序创建适当的单元测试,该应用程序包含javascript文件(带有jasmine测试)和typescript文件(根本没有测试,现在我尝试使用Mocha,但它可以是任何框架).

因此它混合和没有模块的旧angularjs,我决定将所有.ts编译成一个bundle.js文件,因为避免了文件排序问题(当我每个.ts有单个.js文件并将其注入gulp任务时会发生index.html).

我的tsconfig.js:

{
    "compileOnSave": true,
    "compilerOptions": {
        "noImplicitAny": false, 
        "removeComments": false,
        "outFile": "./wwwroot/bundle.js",
        "sourceMap": true,
        "inlineSources": true,
        "module": "amd",
        "moduleResolution": "node",
        "target": "es5",
        "sourceRoot": "./wwwroot"        
    },
    "include": [
      "wwwroot/app/**/*"
    ],
    "exclude": [
      "node_modules/**/*",
      "tests/**/*"      
    ]
}
Run Code Online (Sandbox Code Playgroud)

测试类的示例:

///<reference path="../models/paymentCondition.model.ts"/>
///<reference path="../../../../../node_modules/@types/angular/index.d.ts"/>

'use strict';


module PaymentCondition {

    export class ConnectedCustomersListController {
        name: string;

        static $inject = ['paymentCondition'];
        constructor(private paymentCondition: PaymentConditionModel) {
            this.name = paymentCondition.Name;
            this.bindData();
        }



        bindData() {
            // do something
        }                
    }

    angular
        .module('app.paymentConditions')
        .controller('ConnectedCustomersListController', ConnectedCustomersListController);
}
Run Code Online (Sandbox Code Playgroud)

我的模块声明:

///<reference path="../../../../node_modules/@types/angular/index.d.ts"/>

'use strict';

module PaymentCondition {

    angular.module('app.paymentConditions', ['ui.router', 'ui.bootstrap']);
}
Run Code Online (Sandbox Code Playgroud)

我正在将这个模块"注入"主模块文件,该文件已经在javascript-中了App.module.js.:

(function () {
    'use strict';

    var module = angular.module('app', [       
        'app.paymentCondition',
        'ui.router',     
        'ui.bootstrap',        
    ]);

})();
Run Code Online (Sandbox Code Playgroud)

最后我的测试类:

///<reference path="../../../node_modules/@types/angular/index.d.ts"/>
///<reference path="../../../wwwroot/app/domain/paymentConditions/connectedCustomersList/connectedCustomersList.controller.ts"/>
///<reference path="../../../node_modules/@types/angular-mocks/index.d.ts"/>

import { expect } from 'chai';
import "angular-mocks/index";
import * as angular from "angular";


describe("app.paymentConditions.connectedCustomersList", () => {
    var mock;
    // inject main module
    beforeEach(angular.mock.module('app.paymentConditions'));
    beforeEach(angular.mock.inject(($controller: ng.IControllerService) => {

        mock = {           
            connectedCustomersListModel: {
                columnDefinitions() {
                }
            },
            paymentCondition: {},
            createController(): PaymentCondition.ConnectedCustomersListController {
                return $controller<PaymentCondition.ConnectedCustomersListController >('ConnectedCustomersListController', {
                    connectedCustomersListModel: mock.connectedCustomersListModel,

                });
            }
        };
    }));

    describe("ConnectedCustomersListController", () => {
        var controller: PaymentCondition.ConnectedCustomersListController;
        beforeEach(() => {
            controller = mock.createController();
        });

        it("should be defined", () => {
            expect(controller).not.undefined;
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

当我尝试mocha使用命令运行测试时:

./node_modules/.bin/mocha --compilers ts:ts-node/register ./tests/**/*.spec.ts
Run Code Online (Sandbox Code Playgroud)

我有这个例外:

ReferenceError: define is not defined
    at Object.<anonymous> (C:\Projects\App.Frontend\EasyFrontend\src\EasyFrontend\tests\paymentConditions\connec
edCustomersList\connectedCustomersList.controller.spec.ts:5:1)
    at Module._compile (module.js:643:30)
    at Module.m._compile (C:\Projects\App.Frontend\EasyFrontend\src\EasyFrontend\node_modules\ts-node\src\index.
s:422:23)
    at Module._extensions..js (module.js:654:10)
    at Object.require.extensions.(anonymous function) [as .ts] (C:\Projects\App.Frontend\EasyFrontend\src\EasyFr
ntend\node_modules\ts-node\src\index.ts:425:12)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Module.require (module.js:587:17)
    at require (internal/module.js:11:18)
    at C:\Projects\App.Frontend\EasyFrontend\src\EasyFrontend\node_modules\mocha\lib\mocha.js:231:27
    at Array.forEach (<anonymous>)
    at Mocha.loadFiles (C:\Projects\App.Frontend\EasyFrontend\src\EasyFrontend\node_modules\mocha\lib\mocha.js:2
8:14)
    at Mocha.run (C:\Projects\App.Frontend\EasyFrontend\src\EasyFrontend\node_modules\mocha\lib\mocha.js:536:10)
    at Object.<anonymous> (C:\Projects\App.Frontend\EasyFrontend\src\EasyFrontend\node_modules\mocha\bin\_mocha:
82:18)
    at Module._compile (module.js:643:30)
    at Object.Module._extensions..js (module.js:654:10)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Function.Module.runMain (module.js:684:10)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3
npm ERR! Test failed.  See above for more details.
Run Code Online (Sandbox Code Playgroud)

我知道这是因为我使用amd模块将我的打字稿编译成一个js文件,但我真的不知道如何修复它.或者,如果它无法修复,也许您有一些建议如何将类型脚本"整合"到现有的AngularJs解决方案.

PS.我正在使用mocha和支持的typescript编译器,因为我不知道如何使用这种组合运行jasmine测试.

我的Index.html:

<!DOCTYPE html>
<html>

<head ng-controller="AppCtrl">
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta lang="da" />
    <title>{{ Page.title() }}</title>


   <!-- endbuild -->
    <!-- inject:css -->
    <link rel="stylesheet" type="text/less" href="less/site.less" />
    <!-- endinject -->
    <!-- build:remove -->
    <script src="less/less.js"></script>
    <!-- endbuild -->    
    <!-- bower:js -->
    <script src="lib/jquery/dist/jquery.js"></script>
    <script src="lib/bootstrap/dist/js/bootstrap.js"></script>
    <script src="lib/angular/angular.js"></script>
    <script src="lib/toastr/toastr.js"></script>
    <script src="lib/angular-ui-router/release/angular-ui-router.js"></script>
    <script src="lib/angular-ui-grid/ui-grid.js"></script>
    <script src="lib/angular-bootstrap/ui-bootstrap-tpls.js"></script>
    <script src="lib/sugar/release/sugar-full.development.js"></script>
    <script src="lib/ng-context-menu/dist/ng-context-menu.js"></script>
    <script src="lib/ng-messages/angular-messages.js"></script>
    <script src="lib/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js"></script>
    <script src="lib/bootstrap-datepicker/dist/locales/bootstrap-datepicker.da.min.js"></script>
    <script src="lib/angular-ui-tree/dist/angular-ui-tree.js"></script>
    <script src="lib/angular-sanitize/angular-sanitize.js"></script>
    <script src="lib/color-hash/dist/color-hash.js"></script>
    <script src="lib/angular-ui-mask/dist/mask.js"></script>
    <script src="lib/google-maps-js-marker-clusterer/src/markerclusterer.js"></script>
    <script src="lib/ngDraggable/ngDraggable.js"></script>
    <script src="lib/requirejs/require.js"></script>
    <!-- endbower -->
    <!-- endbuild -->
    <!-- build:site_js js/site.min.js -->
    <!-- inject:app:js -- >   
    <script src="bundle.js"></script>
    <script src="app/app.module.js"></script>  
    <script src="app/app.route.config.js"></script>
    <script src="app/app.module.config.js"></script>
    <script src="app/app.constants.js"></script>
    <script src="app/app.appCtrl.js"></script>       
    <!-- endinject -->
    <!-- endbuild -->
    <!-- endbuild -->
    <!-- build:remove -->
    <script src="init.js"></script>
    <!-- endbuild -->    
</head>

<body>
    <div class="fluid-container">
        <ee-global-context-menu></ee-global-context-menu>
        <ui-view></ui-view>
    </div>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

Alu*_*dad 5

因此它是混合的,并且是没有模块的旧的angularjs

您已经声明您没有使用模块,但实际上您正在使用。

tsconfig.json你已经证明表明您已经配置打字稿到transpile你的代码AMD模块。此外,您index.html实际上是在使用AMD加载程序(即RequireJS)进行相应设置的。

所有这一切都很好。您应该使用模块,使用AngularJS不仅可以,而且很容易。

但是,顺便说一句,ts-node可以带您的TypeScript代码,然后自动进行编译并运行它。执行此操作时,它将从中加载设置tsconfig.json,实例化传递这些设置的TypeScript编译器,编译您的代码,然后将结果传递到Node.js以便执行。

NodeJS不是AMD模块环境。它不支持AMD,并且不提供define功能。

有几种执行测试的有效方法。

一种选择是对ts节点使用不同的配置,特别是告诉它输出CommonJS模块而不是AMD模块。这将产生Node.js可以理解的输出。

就像是

./node_modules/.bin/mocha --compilers ts:ts-node/register --project tsconfig.tests.json
Run Code Online (Sandbox Code Playgroud)

这里tsconfig.tests.json看起来像

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true
  },
  "include": ["tests/**/*.spec.ts"]
}
Run Code Online (Sandbox Code Playgroud)

请记住,AMD和CommonJS模块具有不同的语义,尽管您可能永远不会在测试代码中遇到它们的任何差异,但是您的代码将使用与生产代码不同的加载程序进行测试。

另一个选择是在节点中使用AMD兼容的加载器来运行测试。您可以使用mocha的--require选项来执行此操作。例如

mocha --require requirejs
Run Code Online (Sandbox Code Playgroud)

备注:

您的代码中存在一些错误,即使这些错误不是问题的直接原因,也应予以解决,它们与模块,路径等有关。

  • 不要/// <reference path="..."/>用于加载声明文件。编译器将自动选择它们。

  • 不要使用module关键字在TypeScript代码中创建名称空间。长期以来不建议使用此方法,并删除了它,因为它引起了术语上的混乱。请改用namespace关键字。

  • 切勿混合使用模块语法,import x from 'y'/// <reference path="x.ts"/>来实际加载代码。

    换句话说,在您的测试中,替换

    ///<reference path="../../../wwwroot/app/domain/paymentConditions/connectedCustomersList/connectedCustomersList.controller.ts"/>
    
    Run Code Online (Sandbox Code Playgroud)

    import "../../../wwwroot/app/domain/paymentConditions/connectedCustomersList/connectedCustomersList.controller.ts";
    
    Run Code Online (Sandbox Code Playgroud)

    立刻!

    进行此更改后,您的测试将如下所示

    import "../../../wwwroot/app/domain/paymentConditions/connectedCustomersList/connectedCustomersList.controller.ts";
    import chai from 'chai';
    import "angular-mocks/index"; // just like controller.ts
    import angular from "angular";
    const expect = chai.expect;
    
    Run Code Online (Sandbox Code Playgroud)

    这是严重的。不用考虑,就去做。

  • 考虑将整个代码库转换为适当的模块。AngularJS可以很好地使用这种方法,它将减少工具链中的整体复杂性,同时使您的系统更加完善,并使代码更易于维护和重用。

    想法是最终改变

    namespace PaymentConditions {
      angular.module('app.paymentConditions', ['ui.router', 'ui.bootstrap']);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    import angular from 'angular';
    import uiRouter from 'angular-ui-router';
    import uiBootstrap from 'angular-ui-bootstrap';
    
    import ConnectedCustomersListController from './connectedCustomersList/connectedCustomersList.controller';
    
    const paymentConditions = angular.module('app.paymentConditions', [
        uiRouter,
        uiBootstrap
      ])
      .controller({
        ConnectedCustomersListController
      });
    
    export default paymentConditions;
    
    Run Code Online (Sandbox Code Playgroud)

    与您的控制器

    export default class ConnectedCustomersListController {
      static $inject = ['paymentCondition'];
    
      name: string;
    
      constructor(public paymentCondition: PaymentConditionModel) {
        this.name = paymentCondition.Name;
        this.bindData();
      }
    
      bindData() {
        // do something
      }                
    }
    
    Run Code Online (Sandbox Code Playgroud)