javascript - AngularJS TypeScript 单元测试

标签 javascript angularjs typescript mocha.js tsc

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

因此它混合了一个没有模块的旧 angularjs,我决定将所有 .ts 编译到一个 bundle.js 文件,以避免文件排序问题(当我有单个 .js 文件时发生每个 .ts 并将其与 gulp 任务一起注入(inject) 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/**/*"      
    ]
}

测试类示例:

///<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);
}

我的模块声明:

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

'use strict';

module PaymentCondition {

    angular.module('app.paymentConditions', ['ui.router', 'ui.bootstrap']);
}

我正在将这个模块“注入(inject)”到主模块文件中,该文件已经在 javascript- App.module.js 中。:

(function () {
    'use strict';

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

})();

最后是我的测试课:

///<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;
        });
    });
});

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

./node_modules/.bin/mocha --compilers ts:ts-node/register ./tests/**/*.spec.ts

我有这个异常(exception):

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.

我知道这是因为我正在使用 amd 模块将我的 typescript 编译成一个 js 文件,但我真的不知道如何修复它。或者,如果它无法修复,也许您有一些建议如何将类型脚本“嫁接”到现有的 AngularJs 解决方案。

附言。我将 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>

最佳答案

Hence it hybrid and an old angularjs without modules

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

tsconfig.json您已显示表明您已配置 TypeScript 以将您的代码转换为 AMD 模块。此外,您的 index.html相应地进行设置,因为您实际上使用的是 AMD 加载程序,即 RequireJS。

这一切都很好。您应该使用模块,使用 AngularJS 这样做不仅可能而且简单。

但是,顺便说一下,ts-node 非常棒,它会获取您的 TypeScript 代码,然后自动转译并运行它。当它这样做时,它会从您的 tsconfig.json 加载设置。 ,实例化一个传递这些设置的 TypeScript 编译器,编译您的代码,然后将结果传递给 Node.js 以执行。

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

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

一个选择是对 ts-node 使用不同的配置,具体来说,告诉它输出 CommonJS 模块而不是 AMD 模块。这将产生 Node.js 理解的输出。

有点像

./node_modules/.bin/mocha --compilers ts:ts-node/register --project tsconfig.tests.json

哪里tsconfig.tests.json看起来像

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true
  },
  "include": ["tests/**/*.spec.ts"]
}

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

另一种选择是在节点中使用 AMD 兼容的加载器来运行测试。您可以使用 mocha 的 --require 选项来做到这一点。例如

mocha --require requirejs

备注:

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

  • 不要使用 /// <reference path="..."/>加载声明文件。编译器会自动拾取它们。

  • 不要使用 module在 TypeScript 代码中创建命名空间的关键字。这已被长期弃用并被删除,因为它引入了术语混淆。使用 namespace关键字。

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

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

    ///<reference path="../../../wwwroot/app/domain/paymentConditions/connectedCustomersList/connectedCustomersList.controller.ts"/>
    

    import "../../../wwwroot/app/domain/paymentConditions/connectedCustomersList/connectedCustomersList.controller.ts";
    

    马上!

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

    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;
    

    这很严重。别想,就去做。

  • 考虑将整个代码库转换为适当的模块。 AngularJS 使用这种方法效果很好,它将降低工具链的整体复杂性,同时使您的系统更好地分解,并且您的代码更易于维护和重用。

    这个想法最终会改变

    namespace PaymentConditions {
      angular.module('app.paymentConditions', ['ui.router', 'ui.bootstrap']);
    }
    

    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;
    

    你的 Controller 是

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

关于javascript - AngularJS TypeScript 单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48465835/

相关文章:

javascript - 如果 array.find() 不返回任何内容,如何调用方法?

php - 光标指针更改最佳实践

javascript - Angular-ui-router 没有默认状态并且无法从它转换

angular - 如何在angular2中使用谓词

angularjs - 告诉 ui-router 不要更改状态定义上的位置

javascript - 将 Firebase 中的数据填充到我的空数组中

angular - 订阅相同的服务调用而无需在 Angular 7 中多次调用服务

javascript - 我可以在 jQuery 事件中包装 javascript 事件吗?

javascript - Angular 1 中的响应式源图像,好还是坏?

javascript - jQuery post 和 OnClick 问题