当前位置: 动力学知识库 > 问答 > 编程问答 >

javascript - Properly testing async functions in an angular controller

问题描述:

So I'm trying to set up a test harness for our front end and have run into a total road block. We've decided to use Karma, mocha and chai to do the testing. I'm trying to test the "init" function (not really an init function, just called that) in the following controller:

'use strict';

var app = angular.module('apiWebsite');

app.controller('SidebarController', ['$scope', '$rootScope', '$timeout', 'fileService', '$location', '$q', function($scope, $rootScope, $timeout, fileService, $location, $q){

$scope.versionList = [];

$scope.serviceList = [];

$scope.currentAPIDir = "";

$scope.errorMsg = undefined;

$scope.cats = 0;

.....

Unrelated shit goes here

.....

$scope.init = function() {

return $q((res, err) => {

var applyFunc = function () {

console.log("-=-=-==-=");

var error = fileService.getError();

if (error !== undefined) {

$scope.errorMsg = error.msg;

} else {

$scope.errorMsg = undefined;

$scope.versionList = Object.keys(fileService.getApiDirs());

var search = $location.search();

if (search.hasOwnProperty('version')) {

$scope.setApiVer(search.version, false);

if (search.hasOwnProperty('service')) {

$scope.loadService(search.service, false);

}

} else {

//At this point we can just assume the remainder of the URL is invalid as well (even if it wasn't we cant use it)

//So, we clear it.

$location.url($location.path());

$scope.setApiVer(fileService.getCurrentAPIDir());

}

$scope.currentAPIDir = $scope.versionList[$scope.versionList.indexOf(fileService.getCurrentAPIDir())];

if (!search.hasOwnProperty("method")) {

fileService.notifyApiChange();

}

}

res();

$timeout(() => {

$scope.$apply();

});

};

if (!fileService.isInitialized()) {

fileService.fetchData().then(applyFunc);

} else {

applyFunc();

}

});

};

}]);

which is using this service:

var app = angular.module('apiWebsite');

app.service('fileService', ['$rootScope', '$http', '$state', '$window', 'usersService', '$q', function($rootScope, $http, $state, $window, usersService, $q){

this.currentAPIDir= "";

this.serviceTree = {};

this.selectedService = "";

this.API = "";

this.initialized = false;

this.ramlPath = "";

this.error = undefined;

.....

unrelated shit goes here

.....

this.fetchData = () => {

var token = usersService.getToken();

return $http({

method: 'GET',

url: '/apistuff',

headers: {

'User-Token': token

}

}).then((response) => {

if (response.data.error !== undefined) {

this.error = {msg: response.data.errorMsg, err: response.data.error};

return;

}

this.ramlPath = response.data.path.replace("public/", "");

this.serviceTree = response.data.tree;

this.currentAPIDir = Object.keys(this.serviceTree)[0];

this.loadService(this.serviceTree[this.currentAPIDir].services[0], false);

this.initialized = true;

for (var property in this.serviceTree) {

if (this.serviceTree.hasOwnProperty(property)) {

this.serviceTree[property].services.sort();

}

}

$rootScope.$apply();

}, (error) => {

console.error(error.message);

});

};

this.isInitialized = () => {

return this.initialized;

};

}]);

This is what i currently have for my test, but it's (obviously) not working:

describe('SidebarController', function () {

'use strict';

this.timeout(5000);

var $location, $timeout, $controller, $http, $rootScope, $httpBackend;

var fileService;

var sideBarScope, sideBar, $q, test;

var interval;

beforeEach(function() {

module('apiWebsite')

inject(function (_$httpBackend_, _$controller_, _$rootScope_, _$timeout_, _$location_, _fileService_, _$q_) {

$rootScope = _$rootScope_;

sideBarScope = $rootScope.$new();

$location = _$location_;

$timeout = _$timeout_;

$controller = _$controller_;

fileService = _fileService_;

$httpBackend = _$httpBackend_;

$q = _$q_;

sideBar = $controller('SidebarController', {

'$scope': sideBarScope

});

});

$httpBackend.whenGET("app/components/home/home.html").respond({ hello: 'World' });

$httpBackend.whenGET("app/shared/navbar/navbar.html").respond({ hello: 'World' });

$httpBackend.whenGET("app/shared/footer/footer.html").respond({ hello: 'World' });

$httpBackend.expectGET("app/components/home/home.html");

$httpBackend.expectGET("app/shared/navbar/navbar.html");

$httpBackend.expectGET("app/shared/footer/footer.html");

//had to do this otherwise nothing would ever happen.

interval = setInterval(() => {

$rootScope.$apply();

}, 100);

});

afterEach(function() {

$httpBackend.flush();

clearInterval(interval);

});

it('Starts Empty', function () {

expect(sideBarScope.versionList).to.be.empty;

expect(sideBarScope.serviceList).to.be.empty;

expect(sideBarScope.errorMsg).to.be.empty;

expect(sideBarScope.currentAPIDir).to.be.empty;

});

it("Is not empty after initializing", function(done) {

$httpBackend.whenGET("/apistuff").respond({"errorMsg":"Failed to authenticate","error":{"name":"JsonWebTokenError","message":"jwt must be provided"}});

$httpBackend.expectGET("/apistuff");

sideBarScope.init().then((res) => {

try {

expect(sideBarScope.errorMsg).to.not.be.empty;

done();

} catch (e) {

done(e);

}

});

});

});

At this stage, I've figured out how to test normal functions, functions that are async and return one promise (this is what the interval with rootScope.$apply is for) but not this magical nested asycn stuff that's going on in my init.

分享给朋友:
您可能感兴趣的文章:
随机阅读: