Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test functions with $http.jsonp in AngularJS?

Here is part of my controller:

    $scope.isLoggedIn = function () {
        return Boolean($sessionStorage.isLoggedIn);
    };

    $scope.login = function () {
        //If the browser fills in the username and password field then angular's model will not be updated so we need
        //to manually pull them out of the DOM :(. See this issue for details: https://github.com/angular/angular.js/issues/1460
        var username = $('#usernameInput').val();
        var password = $('#passwordInput').val();
        $scope.loginErrMsg = null;

        $http.jsonp("http://ldap-auth.otpp.me" + '/login?callback=JSON_CALLBACK&username=' + encodeURIComponent(username) + '&password=' + encodeURIComponent(password))
            .success(function (data, isLoggedIn) {
                // This app is for internal use only, security is not a major concern.
                // So using sessionStorage to store the login status is OK.
                $sessionStorage.displayName = data.displayName;
                $sessionStorage.isLoggedIn = true;
            })
            .error(function () {
                $scope.loginErrMsg = "Invalid username or password";
            });
    };

Here is my test:

describe('Controller: MainCtrl', function () {

// load the controller's module
beforeEach(module('uiappApp'));

var MainCtrl,
    $httpBackend,
    scope;

// Initialize the controller and a mock scope
beforeEach(inject(function ($injector, $controller, $rootScope) {
    scope = $rootScope.$new();
    MainCtrl = $controller('MainCtrl', {
        $scope: scope
    });
    $httpBackend = $injector.get('$httpBackend');

}));
it('should login', function () {
    var isLoggedIn = scope.isLoggedIn();
    expect(isLoggedIn).toBe(false);

    var username = $('<input type="text" id="usernameInput"/>');
    var password = $('<input type="text" id="passwordInput"/>');
    $('body').html('<div>')
        .find('div')
        .append(username)
        .append(password);
    username.val('username');
    password.val('password');
    var response =  $httpBackend.expectJSONP('http://ldap-auth.otpp.me/login?callback=JSON_CALLBACK&username=username&password=password');
    response.respond({displayName: 'userX'}, {'A-Token': 'xxx'});
    scope.login();
    expect(scope.isLoggedIn()).toEqual(true);
    $('body').empty();
    });
});

However the test always fails (Expected false to equal true.). It seems that I didn't get $httpBackend working. But I cannot figure out where was wrong. Basically I want to mock $http.jsonp() response to provide a mocked-up json when unit testing and then test the user login.

Thanks very much if you can help!

@HackedByChinese After folloing your step, I have:

it('should login', function () {
    var isLoggedIn = scope.isLoggedIn();
    expect(isLoggedIn).toBe(false);

    var username = $('<input type="text" id="usernameInput"/>');
    var password = $('<input type="text" id="passwordInput"/>');
    $('body').html('<div>')
        .find('div')
        .append(username)
        .append(password);
    username.val('username');
    password.val('password');
    var response =  $httpBackend.expectJSONP('http://ldap-auth.otpp.me/login?callback=JSON_CALLBACK&username=username&password=password');
    response.respond({displayName: 'userX'}, {'A-Token': 'xxx'});
    scope.login();
    $httpBackend.flush();
    expect(scope.isLoggedIn()).toEqual(true);
    $('body').empty();
});

But I got the following error message:

    Error: Unexpected request: GET views/main.html
No more request expected
    at $httpBackend (C:/repo/docserver-ui/UIApp/app/bower_components/angular-mocks/angular-mocks.js:1177:9)
    at sendReq (C:/repo/docserver-ui/UIApp/app/bower_components/angular/angular.js:7967:9)
    at $get.serverRequest (C:/repo/docserver-ui/UIApp/app/bower_components/angular/angular.js:7708:16)
    at deferred.promise.then.wrappedCallback (C:/repo/docserver-ui/UIApp/app/bower_components/angular/angular.js:11100:81)
    at deferred.promise.then.wrappedCallback (C:/repo/docserver-ui/UIApp/app/bower_components/angular/angular.js:11100:81)
    at C:/repo/docserver-ui/UIApp/app/bower_components/angular/angular.js:11186:26
    at Scope.$get.Scope.$eval (C:/repo/docserver-ui/UIApp/app/bower_components/angular/angular.js:12175:28)
    at Scope.$get.Scope.$digest (C:/repo/docserver-ui/UIApp/app/bower_components/angular/angular.js:12004:31)
    at Function.$httpBackend.flush (C:/repo/docserver-ui/UIApp/app/bower_components/angular-mocks/angular-mocks.js:1435:16)
    at null.<anonymous> (C:/repo/docserver-ui/UIApp/test/spec/controllers/main.js:43:22)
Process finished with exit code 0

And here is my partial html:

            <form class="navbar-form navbar-right" role="form" ng-show="!isLoggedIn()">
                <p class="navbar-text" id="loginErrMsg">{{loginErrMsg}}</p>
                <div class="form-group">
                    <input type="text" placeholder="User Name" id="usernameInput"
                           class="form-control"
                           ng-class="{'error-fields': loginErrMsg}">
                </div>
                <div class="form-group">
                    <input type="password" placeholder="Password" id="passwordInput" class="form-control" ng-class="{'error-fields': loginErrMsg}">
                </div>
                <button type="submit" class="btn btn-success" ng-click="login()">Sign in</button>
        </form>
like image 669
Hengwei Avatar asked Mar 06 '26 14:03

Hengwei


1 Answers

After calling scope.login() and before your expect(...), you need to call $httpBackend.flush() for it to respond with the specified expectations.

This is not specific to JSONP, but unit testing $http calls in general.

To summarize, here are the steps to unit test code that makes $http requests:

  1. Set up your expectations using $httpBackend.
  2. Invoke the code that makes the $http call(s).
  3. Call $httpBackend.flush() to make it respond to any pending requests with the expectations you set up.
  4. Make your test assertions.

See the docs regarding flush.

like image 77
moribvndvs Avatar answered Mar 09 '26 04:03

moribvndvs



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!