如何测试父元素对组件绑定的更改?

2024-01-07

我有一个如下所示的组件,想测试一下$onChange方法在绑定的情况下执行myBinding变化。

我尝试了一整个早上,但找不到解决这个问题的方法。

angular
    .module('project.myComponent', [])
    .component('myComponent', {
        bindings: {
            myBinding: '<'
        },
        template: '<div>{{$ctrl.result}}</div>',
        controller: myComponentController
    });

function myComponentController($filter, someService) {
    var ctrl = this;
    ctrl.result = 0;

    $ctrl.$onChange = function (changes) {
        if(angular.isDefined(changes.myBinding)) {
            if(angular.isDefined(changes.myBinding.currentValue)) {
                if(angular.isDefined(changes.myBinding.currentValue != changes.myBinding.previousValue)) {
                    myService.doSomething(changes.myBinding.currentValue).then(
                        function(data) {
                            ctrl.result = changes.myBinding.currentValue * 3;
                        }
                    );                  
                }
            }
        }
    }
}

我希望我的测试就像组件父级一样,它改变了绑定的值。

require('angular-mocks');

describe('myComponment', function() {
    var element, scope;

    beforeEach(inject(function(_$rootScope_, _$compile_) {

    }));

    fit('should display the controller defined title', function() {        
        // prepare test and set myBinding to 10
        expect(component.result).toBe(30);
    });
});

那可能吗?如何? 有什么提示吗? Plunker、CodePen 或其他示例?


测试 AngularJS 组件与测试指令没有太大区别。

要测试控制器的方法/属性,您可以使用访问组件控制器的实例element.controller("componentName") method https://docs.angularjs.org/api/ng/function/angular.element#methods (componentName- 是驼峰命名法指令/组件名称)。

这是使用的示例$compile service https://docs.angularjs.org/api/ng/service/%24compile测试组件并$onChanges hook:

angular.module('myApp', [])
.component('myComponent', {
    bindings: {
        myBinding: '<'
    },
    template: '<div>{{$ctrl.result}}</div>',
    controller: 'myComponentController'
})
.controller('myComponentController', ['$filter', 'myService', function myComponentController($filter, myService) {
    var ctrl = this;

    ctrl.$onInit = onInit;
    ctrl.$onChanges = onChanges;

    function onInit() {
        ctrl.result = ctrl.myBinding;
    }

    function onChanges(changes) {
        if (angular.isDefined(changes.myBinding)) {
            if (angular.isDefined(changes.myBinding.currentValue)) {
                if (!angular.equals(changes.myBinding.currentValue, changes.myBinding.previousValue)) {
                    myService.doSomething(changes.myBinding.currentValue).then(
                        function (data) {
                            ctrl.result = data; 
                        }
                    );
                }
            }
        }
    }
}])
.service('myService', ['$timeout', function ($timeout) {
    return {
        doSomething: function (x) {
            return $timeout(function () {
                return x * 3;
            }, 500);
        }
    };
}]);


/*
TEST GO HERE 
*/

describe('Testing a component controller', function() {
  var $scope, ctrl, $timeout, myService;
  
    beforeEach(module('myApp', function ($provide) {

    }));
  
    beforeEach(inject(function ($injector) {
        myService = $injector.get('myService');
        $timeout = $injector.get('$timeout');
    }));
  
    describe('with $compile', function () { 
        var element;
        var scope;
        var controller;
        
        beforeEach(inject(function ($rootScope, $compile) {
            scope = $rootScope.$new();
            scope.myBinding = 10;
            element = angular.element('<my-component my-binding="myBinding"></my-component>');
            element = $compile(element)(scope);
            controller = element.controller('myComponent');
            scope.$apply();
        }));
      
        
         it('should render template', function () {
           expect(element[0].innerText).toBe('10'); //initial
           $timeout.flush(); //onchanges happened and promise resolved from the service
           //undefined -> 10
           expect(element[0].innerText).toBe('30'); 
         });
         
         
         it('should reflect to changes', function () {
           spyOn(myService, "doSomething").and.callThrough();
           scope.myBinding = 15; //change the binding
           scope.$apply(); //we need to call $apply to pass the changes down to the component
           $timeout.flush();
           expect(myService.doSomething).toHaveBeenCalled(); // check if service method was called 
           expect(controller.result).toBe(45); // check controller's result value 
         });
         
    })

});
.as-console-wrapper {
  height:0;
}
<!DOCTYPE html>
<html>

  <head>
    <!-- jasmine -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.js"></script>
    <!-- jasmine's html reporting code and css -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine-html.js"></script>
    <link href="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.css" rel="stylesheet" />
    
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/boot.js"></script>
    <!-- angular itself -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
    <!-- angular's testing helpers -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-mocks.js"></script>
  </head>

  <body>
    <!-- bootstrap jasmine! -->
  <script>
    var jasmineEnv = jasmine.getEnv();
    
    // Tell it to add an Html Reporter
    // this will add detailed HTML-formatted results
    // for each spec ran.
    jasmineEnv.addReporter(new jasmine.HtmlReporter());
    
    // Execute the tests!
    jasmineEnv.execute();
  </script>
  </body>

</html>

您还可以使用测试您的组件$componentController service https://docs.angularjs.org/api/ngMock/service/%24componentController。但在这种情况下,您需要在测试中显式调用生命周期挂钩,例如:

ctrl = $componentController('myComponent', {$scope: scope}, { myBinding: 10 });
ctrl.$onInit();

To test $onChanges钩子,您将需要传递一个“正确”构造的更改对象作为参数:

angular.module('myApp', [])
    .component('myComponent', {
        bindings: {
            myBinding: '<'
        },
        template: '<div>{{$ctrl.result}}</div>',
        controller: 'myComponentController'
    })
    .controller('myComponentController', ['$filter', 'myService', function myComponentController($filter, myService) {
        var ctrl = this;

        ctrl.$onInit = onInit;
        ctrl.$onChanges = onChanges;

        function onInit() {
            ctrl.result = ctrl.myBinding;
        }

        function onChanges(changes) {
            if (angular.isDefined(changes.myBinding)) {
                if (angular.isDefined(changes.myBinding.currentValue)) {
                    if (!angular.equals(changes.myBinding.currentValue, changes.myBinding.previousValue)) {
                        myService.doSomething(changes.myBinding.currentValue).then(
                            function (data) {
                                ctrl.result = data;
                            }
                        );
                    }
                }
            }
        }
    }])
    .service('myService', ['$timeout', function ($timeout) {
        return {
            doSomething: function (x) {
                return $timeout(function () {
                    return x * 3;
                }, 500);
            }
        };
    }]);


/*
TEST GO HERE 
*/

describe('Testing a component controller', function () {
    var $scope, ctrl, $timeout, myService;

    beforeEach(module('myApp', function ($provide) {

    }));

    beforeEach(inject(function ($injector) {
        myService = $injector.get('myService');
        $timeout = $injector.get('$timeout');
    }));

    describe('with $componentController', function () {
        var scope;
        var controller;

        beforeEach(inject(function ($rootScope, $componentController) {
            scope = $rootScope.$new();
            scope.myBinding = 10;

            controller = $componentController('myComponent', {$scope: scope}, {myBinding: 10});
            controller.$onInit();
        }));

        it('should reflect to changes', function () {
            spyOn(myService, "doSomething").and.callThrough();
            controller.$onChanges({myBinding: {currentValue: 15, previousValue: 10}});
            $timeout.flush(); // resolve service promise 
            expect(myService.doSomething).toHaveBeenCalled(); // check if service method was called 
            expect(controller.result).toBe(45); // check controller's result value 
        });

    })

});
.as-console-wrapper {
  height:0;
}
<!DOCTYPE html>
<html>

  <head>
    <!-- jasmine -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.js"></script>
    <!-- jasmine's html reporting code and css -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine-html.js"></script>
    <link href="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.css" rel="stylesheet" />
    
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/boot.js"></script>
    <!-- angular itself -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
    <!-- angular's testing helpers -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-mocks.js"></script>
  </head>

  <body>
    <!-- bootstrap jasmine! -->
  <script>
    var jasmineEnv = jasmine.getEnv();
    
    // Tell it to add an Html Reporter
    // this will add detailed HTML-formatted results
    // for each spec ran.
    jasmineEnv.addReporter(new jasmine.HtmlReporter());
    
    // Execute the tests!
    jasmineEnv.execute();
  </script>
  </body>

</html>

P.S.: $onChange不是组件生命周期挂钩的有效名称。它应该$onChanges https://docs.angularjs.org/guide/component#component-based-application-architecture.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何测试父元素对组件绑定的更改? 的相关文章

  • Angular - 过滤器从数组中删除空白字符串

    我有一个数组对象 数组可以包含空格 我如何创建 Angular 过滤器来删除空格以确定数组的长度 scope myData 1 1 4 4 N 4 6 8 2 2 4 6 0 6 5 4 2 8 2 3 3 F D 3 5
  • 如何在要测试的 React 组件内模拟自定义钩子?

    如果您有一个调用获取数据的自定义挂钩的 React 组件 那么在测试 React 组件时模拟内部自定义挂钩结果的最佳方法是什么 我看到两种主要方法 1 Jest mock 自定义钩子 这似乎是最推荐的方法 但它似乎要求测试对内部实现细节以及
  • 在 Chrome 扩展中使用页面的 Angular JS

    我有一个 HTML 页面 其中有一些使用 Angular 配置的 DOM 现在我正在构建一个 chrome 扩展来修改文本框中的值 element value newValue不起作用 因为文本框是用 Angular 设计的 在阅读了一些资
  • 身份验证在不应该返回“401(未经授权)”时返回

    我第一次设置身份验证功能 在用户登录后得到了一些意外的结果 一位同事给了我一个具有工作身份验证的应用程序 以模仿我的应用程序 看起来我所做的一切都是正确的 我在前端使用 AngularJS 在后端框架使用 SailsJS 并且护照JS ht
  • ngRepeat 中的指令时的绑定问题

    这就是它的样子 这是Plunker http plnkr co edit IPwDLT p preview parent scope ng repeat directive 在指令中 有一个属性与父作用域中的变量进行双向绑定 但这并没有像我
  • ui-sref 和变量状态参数名称

    我想呈现一个链接 例如 a 其中州名myState和钥匙myKey是变量 有办法做到这一点吗 我发现自己处于同样的情况 我也无法完成这一点 尝试使用 ng click 移动代码 并在 ng click 函数内部使用 stage go htt
  • 无需编译的 ES6 单元测试

    我无法找到任何 Mocha 或任何其他通过 Gulp 直接在 ES6 代码上运行的单元测试框架的示例 没有 Babel Webpack 等 我找到了一个在浏览器中使用 ES6 代码运行 Mocha 的示例 经过一些修改 但它不是自动化的 有
  • Swift 单元测试 - 如何断言 CGColor 是它应该的样子?

    使用 Xcode V7 2 尝试进行单元测试 需要验证是否已设置正确的颜色 并收到以下消息 Cannot invoke XCTAssertEqual with an argument list of type CGColor CGColor
  • RxJava android mvp 单元测试 NullPointerException

    我是 mvp 单元测试的新手 我想对演示者进行一个非常基本的测试 它负责登录 我只想断言 view onLoginSuccess 这是演示者代码 public LoginPresenter LoginViewContract loginVi
  • 如何跨多个文件跨越 javascript 命名空间?

    我永远忽略了javascript 几年前我开始使用 jQuery 这样我就可以过得去 但随着我开始更多地进行 TDD 我昨天决定真正深入研究 javascript 之后可能还有咖啡脚本 在我的 ASP NET Web 窗体应用程序中 我有很
  • AngularJS:服务、提供商、工厂

    之间有什么区别Service Provider and Factory在 AngularJS 中 从我得到的 AngularJS 邮件列表一个惊人的线程 https groups google com forum msg angular 5
  • Angular 指令,属性更新时不调用链接

    在以下示例中 http plnkr co edit OZjg6sUgl35GIriaabQg p preview http plnkr co edit OZjg6sUgl35GIriaabQg p preview 我有 2 个指令 show
  • 如何使用 AngularJS ngView 为未经授权的用户隐藏模板?

    我有一个基本的 PHP 应用程序 其中用户登录信息存储在 HTTP 会话中 该应用程序有一个主模板 例如index html 它使用 ngView 切换子视图 如下所示 div div 现在 这个主模板可以通过基本的 PHP 控件进行保护
  • “move(-1)”作为 AngularJS 表达式有什么问题吗?

    我收到此错误 parse ueoe Unexpected end of expression move 从这段代码来看
  • 模拟 DBSet,EF 模型优先

    正如标题所说 我遵循模型优先方法 所以我的模型类是自动生成的 如果我想嘲笑DBContext衍生的MyModelContainer其中包含DBSets实体类 阅读一些内容 为了进行单元测试 您需要将其更改为IDBSet 是否可以做到这一点
  • 包括 PHPUnit 在内的麻烦

    我想开始为我的代码编写测试 因此我使用以下命令安装了最新的 PHPUnit wget http pear phpunit de get phpunit phar chmod x phpunit phar mv phpunit phar us
  • 对 Java Servlet 进行单元测试

    我想知道对 servlet 进行单元测试的最佳方法是什么 只要内部方法不引用 servlet 上下文 测试内部方法就不是问题 但是测试 doGet doPost 方法以及引用上下文或使用会话参数的内部方法呢 有没有办法简单地使用经典工具 例
  • 使用 AngularJS 制作 Windows Phone 全景图

    我正在尝试在 AngularJS 应用程序中复制 Windows Phone Ui 这是一个example http www expertreviews co uk gallery features 1295629 designing wi
  • 根据用户是否经过身份验证隐藏或显示链接 - AngularJs

    我目前正在研究一个AngularJS应用程序中 我遇到了以下障碍 我们有一个login当用户提交页面时 我们调用 Web api 并对用户进行身份验证 我们目前正在使用声明身份验证来设置 cookie 等 这些内容按预期工作 但是我遇到的问
  • 如何避免从模拟对象列表返回模拟

    我正在尝试模拟 责任驱动的设计 在对象需要服务来检索其他对象的情况下 我似乎无法避免从模拟返回模拟 一个例子是检查上个月的账单是否已支付的对象 它需要一个检索账单列表的服务 所以我需要在测试中模拟 billRetrievalService

随机推荐

  • 如何在 Tomcat 的 META-INF/context.xml 中指定路径

    我正在使用 Tomcat 7 希望在 war 文件本身中设置 war 文件的上下文根 并让 Tomcat 自动部署并选择此路径 我想我找到了通过将 context xml 放入包含的 war 的 META INF 目录中来做到这一点的方法
  • DataGrid DataGridTemplateColumn 组合框

    我的方法有问题ComboBoxes in a DataGrid 我用几张图来解释一下 这是起点 现在 如果我想添加新行 我单击最后一行并按 Enter 键 添加一个新行 并在此处选择类型表并为其命名为 MY TABLE 然后按 Enter
  • 这是互斥体的正确使用吗?

    我遇到的情况是 我可能同时运行一个程序的多个实例 并且重要的是 不要同时在多个实例中执行一个特定函数 这是使用互斥锁来防止这种情况发生的正确方法吗 lock this GetType log Info Doing Sync DoSync l
  • 带下划线的数据全文搜索

    我有一个索引表 其中索引列之一可以包含带下划线的数据 ID Name 1 01 A3L 2 02 A3L 3 03 A3L 4 05 A3L 5 some name 6 another name 7 a name 当我使用以下查询搜索该表时
  • ebXml OpenSource java实现[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 在我们的项目中 我们正在寻找 OASIS ebXmlRegistry 3 0 规范的开源 java 实
  • 如何将所有新文件添加到 SVN

    我正在使用 ORM 它从 CLI 生成大量文件 有没有一种简单的方法来运行svn add目录中的所有文件显示为 当我跑步时svn status Edit这些文件存在于目录树中 因此添加 对于一个目录是行不通的 这将在指定的目录树下添加所有未
  • Node.js 的 Rails 控制台的等效项

    我正在尝试 Node js Express 框架 并寻找允许我通过控制台与模型交互的插件 类似于 Rails 控制台 NodeJS 世界里有这样的东西吗 如果没有 我如何与 Node js 模型和数据交互 例如手动添加 删除对象 测试数据方
  • JavaFX Alert 截断消息? [复制]

    这个问题在这里已经有答案了 我注意到如果我尝试展示Alert对于很长的消息 它往往会被截断 在单词边界处 Example import javafx application Application import javafx scene c
  • C 中 : 运算符的使用 [重复]

    这个问题在这里已经有答案了 可能的重复 结构体字段后面的 number 是什么意思 https stackoverflow com questions 3029442 what does number after a struct fiel
  • 使用条件更改 numpy 数组中的每个值

    我有一个从图像中获得的二维数组 现在它有 0 和 255 我想将所有 255 更改为 1 这对于for loop for i in range lenX for j in range lenY if img i j 255 img i j
  • 我可以在源perl程序中插入断点吗?

    我想要 perl 程序在某些条件满足时启动调试器 其他一些语言有库支持的 debug 语句 perl 中是否有类似的语句 如果我理解正确 您需要在代码中使用特定的调试器变量 DB single 在代码中将其设置为真值将导致调试器在该行停止
  • 使用 Powermock 模拟类的构造函数时出现 ExceptionInInitializerError。如何修复它?

    这是我的案例 我有一个 AbstractController 类 它有一个子类Controller 在 AbstractController 的方法之一中 实例化了一个新的 ApplicationLock 我想在为控制器编写 ut 时模拟
  • 将 CURLOPT_CAINFO 与更新的 CA 捆绑包一起使用会导致证书验证失败

    我使用 cURL 在 WordPress 插件中验证 PayPal 交易 最近 我开始收到有关用户因无法验证交易而无法完成购买流程的错误报告 我追踪到错误 SSL certificate problem verify that the CA
  • 从 TextView 到 EditText 的共享元素转换错误

    我正在尝试在TextView and a EditText but when showing the resulting Activity the text in theEditViewhave been shifted up See at
  • 我可以调用 jdbc 中的存储过程来使用 mysql 返回表吗?

    我是使用 jdbc 执行 mysql 存储过程的新手 我的问题是 是否可以调用返回 jdbc 中的表的存储过程 我搜索了很多 我知道我可以使用返回一定数量的参数registerOutParameter使用过程 但是包含几行的整个表怎么样 我
  • Facebook 应用程序如何向用户的所有好友发送消息?

    我们正在尝试构建一个应用程序 该应用程序可以根据登录用户的请求并在应用程序中进行适当的祝福 向任何或所有用户的 FB 好友发送一条消息 声明他们已收到一份礼物 我们已经能够以墙贴的形式向少数朋友发送此消息 通知和消息在 API 中不可用 然
  • 如何编写像 init.d 中使用的那样的 bash 脚本?

    我必须编写一个 bash 脚本来制作很多东西 我想像初始化脚本一样打印消息 例如 Doing A OK Doing B ERROR 你知道有什么方法可以做到这一点吗 提前致谢 在我所有的 Linux 机器上 执行此操作的代码位于文件中 et
  • 如何将依赖项注入 iOS 视图控制器?

    我的视图控制器需要向几个模型对象发送消息 如何获取视图控制器内这些模型对象的引用 这些模型对象是 单例 因为系统中一次只能有它们的一份副本 并且它们由多个视图控制器使用 所以我无法在每个视图控制器的 init 方法中实例化它们 我无法使用构
  • 如何在vb.net中检索mysql数据?

    我试图检索具有特定列的 mysql 数据并显示到 vb net 中的文本框 我应该怎么做才能取回它 Dim connect As New MySqlConnection server localhost user id root passw
  • 如何测试父元素对组件绑定的更改?

    我有一个如下所示的组件 想测试一下 onChange方法在绑定的情况下执行myBinding变化 我尝试了一整个早上 但找不到解决这个问题的方法 angular module project myComponent component my