最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to test angularjs component with DOM - Stack Overflow

programmeradmin4浏览0评论

I am trying to get familiar with testing an AngularJS application. While testing ponent logic is clear more or less, I have a trouble with html templates and model binding, because I'd like to test html binding together with controller's logic.

Test is run by karma in a real browser, so testing environment supports DOM.

It looks like it's not possible, doesn't it?

describe('sign-up', function () {
        angular.mock.module('myApp');
        angular.mock.inject(function($ponentController, $rootScope, $document) {
            let scope = $rootScope.$new();
            let signUp = $ponentController('signUp', {$scope: scope});
            console.log(`signup = ${signUp}`);
            for (let [k,v] of signUp) {
                // there is no field signUp.firstName
                // but inside the controller code referencing  
                // this.firstName is working
                console.log(`signup.${k} = ${v}`);
            }
            // jquery cannot find #firstName node
            // $('#firstName').val('dan') gets the same oute
            $document.find('#firstName').val('dan');
            expect($document.find('#firstName').val()).toBe('dan');
            // without DOM form submission is not possible
        });
    });
});

Controller ponent:

angular.
    module('myApp').
    ponent('signUp', {
        templateUrl: template,
        controller: [
            function () {
                this.form = {};
                var self = this;
}]});

Template:

  <form novalidate name="$ctrl.form" >
    <div class="form-group">
      <input type="text" class="form-control"
             ng-model="$ctrl.firstName"
             required
             name="firstName"
             id="firstName"
             />
    </div>
  </form>

I am trying to get familiar with testing an AngularJS application. While testing ponent logic is clear more or less, I have a trouble with html templates and model binding, because I'd like to test html binding together with controller's logic.

Test is run by karma in a real browser, so testing environment supports DOM.

It looks like it's not possible, doesn't it?

describe('sign-up', function () {
        angular.mock.module('myApp');
        angular.mock.inject(function($ponentController, $rootScope, $document) {
            let scope = $rootScope.$new();
            let signUp = $ponentController('signUp', {$scope: scope});
            console.log(`signup = ${signUp}`);
            for (let [k,v] of signUp) {
                // there is no field signUp.firstName
                // but inside the controller code referencing  
                // this.firstName is working
                console.log(`signup.${k} = ${v}`);
            }
            // jquery cannot find #firstName node
            // $('#firstName').val('dan') gets the same oute
            $document.find('#firstName').val('dan');
            expect($document.find('#firstName').val()).toBe('dan');
            // without DOM form submission is not possible
        });
    });
});

Controller ponent:

angular.
    module('myApp').
    ponent('signUp', {
        templateUrl: template,
        controller: [
            function () {
                this.form = {};
                var self = this;
}]});

Template:

  <form novalidate name="$ctrl.form" >
    <div class="form-group">
      <input type="text" class="form-control"
             ng-model="$ctrl.firstName"
             required
             name="firstName"
             id="firstName"
             />
    </div>
  </form>
Share Improve this question edited Jan 15, 2018 at 10:14 Daniil Iaitskov asked Jan 15, 2018 at 9:37 Daniil IaitskovDaniil Iaitskov 6,11910 gold badges44 silver badges53 bronze badges 3
  • You have to take a look at $pile service. $ponentController will not help you in testing your ponent templates. Take a look at my answer here. The first example shows how to test template (called "should render template"). – Stanislav Kvitash Commented Jan 15, 2018 at 10:23
  • The referenced answer calls "signUp.$onInit()". My setup looks the same, while my ponent doesn't have $onInit(). – Daniil Iaitskov Commented Jan 15, 2018 at 10:33
  • Check the answers, I've setup an example, probably it will be more clear then my first ment :) – Stanislav Kvitash Commented Jan 15, 2018 at 11:06
Add a ment  | 

2 Answers 2

Reset to default 4

You can use $pile service to test your ponent templates ($ponentController creates instances of ponent controllers without creating any markup, even $pile will not attach it to $document, so you have to use angular.element to check your template).

Here is a working example for your ponent:

angular.module('myApp', [])
    .ponent('signUp', {
        template: '<form novalidate name="$ctrl.form" >\n' +
        '    <div class="form-group">\n' +
        '      <input type="text" class="form-control"\n' +
        '             ng-model="$ctrl.firstName"\n' +
        '             required\n' +
        '             name="firstName"\n' +
        '             id="firstName"\n' +
        '             />\n' +
        '    </div>\n' +
        '  </form>',
        controller: 'SignUpController'
    })
    .controller('SignUpController', [function myComponentController() {
        var ctrl = this;
        ctrl.form = {};
    }]);

/*
TESTS GO HERE
*/

describe('Testing a ponent controller', function() {

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

    }));

    beforeEach(inject(function ($injector) {

    }));

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

        beforeEach(inject(function ($rootScope, $pile) {
            scope = $rootScope.$new();
            element = angular.element('<sign-up></sign-up>');
            element = $pile(element)(scope);
            controller = element.controller('signUp');
            console.log(element);
            scope.$apply();
        }));

        it('should render template', function () {
            expect(element.find('input').val()).toBe('');
            controller.firstName = 'Dan';
            scope.$apply();
            expect(element.find('input').val()).toBe('Dan');
        });
    })
    
});
.as-console-wrapper {
  height:0;
}
<!DOCTYPE html>
<html>

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

You need create and pile element:

let element = angular.element('<sign-up></sign-up>');
const $pile = $injector.get('$pile');
element = $pile(element)($scope);

controller = element.controller('signUp');

P.S. $document.find('#firstName') wont return anything since you dont add this element anywehere. Use element.find instead

发布评论

评论列表(0)

  1. 暂无评论