使用AngularJS $ http和Jasmine进行集成测试

时间:2015-08-02 12:31:56

标签: angularjs jasmine integration-testing

我有一个AngularJS服务,通过$ http这样调用服务器

function DefineCourseService($http) {
  var service = {
    getCourses: getCourses
  };

  function getCourses(id) {
    return $http({
      url: '/api/Course',
      method: 'GET'
    });
  }
}

并且服务器返回:

[{Code:'123',Title:'Test'}]

我想使用Jasmine编写集成测试,从服务器获取响应并检查其值。测试文件如下:

(function() {
  'use strict';
  define(['angular-mocks', 'defineCourse.service'], function() {
    describe("Course service", function() {
      var courseService, data, deferredResolution, parentScope;

      beforeEach(function() {
        module('modabber.services');

      });

      beforeEach(inject(function($q, $rootScope, DefineCourseService) {
        courseService = DefineCourseService;
        deferredResolution = $q.defer();
        parentScope = $rootScope;
      }));

      it("get courses", function() {
        spyOn(courseService, 'getCourses').and.callThrough();
        deferredResolution.resolve();
        courseService.getCourses().then(function(result) {
          data = result;
        });
        expect(courseService.getCourses).toHaveBeenCalled();
        expect(data).toBeUndefined();

        parentScope.$digest();
        expect(data).toBeDefined();
        done();
      });
    });
  });
})();

最后我的karma.conf.js:

module.exports = function(config) {
  config.set({
    basePath: '../',
    frameworks: ['jasmine', 'requirejs'],
    files: [
      'karma/test-main.js', {
        pattern: 'WebApiControllers/**/*.js',
        included: false
      }, {
        pattern: 'scripts/vendor/*.js',
        included: false
      }, {
        pattern: 'bower_components/ngMidwayTester/src/ngMidwayTester.js',
        included: false
      }, {
        pattern: 'bower_components/**/*.min.js',
        included: false
      }, {
        pattern: 'scripts/*.js',
        included: false
      }, {
        pattern: 'app/services/*.js',
        included: false
      }, {
        pattern: 'app/directives/*.js',
        included: false
      },

    ],
    exclude: ['scripts/main.js'],
    preprocessors: {    },
    reporters: ['progress'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
}

但它始终失败,因为“数据”未定义,所以问题是什么?

2 个答案:

答案 0 :(得分:6)

angular-mock不会进行真正的ajax调用,因为它可能会使单元测试不准确。为了让Angular调用Web服务并仍然能够保持良好的测试,我建议使用ngMidwayTester。将它与Jasmine的异步支持(done()功能)结合使用,即可进行测试。

describe('Course service', function() {
    var tester;
    var courseService;

    beforeEach(function() {
        tester = ngMidwayTester('modabber.services');
        courseService = tester.inject('DefineCourseService');
    });

    afterEach(function() {
        tester.destroy();
        tester = null;
    });

    it('get courses', function(done) {
        courseService.getCourses()
            .then(function(result) {
                expect(result).toBeDefined();
                expect(result.Code).toBe('123');
                done();
            }, function() {
                done.fail('Web service call failed!');
            });
    });
});

答案 1 :(得分:2)

说明

您正在异步回调函数中设置data,但在外部进行评估。 想象一下你的异步调用需要3秒......这将是它的生命周期:

  1. getCourses名为
  2. 数据未定义且评估失败
  3. 您的考试结束
  4. ... 3秒后... getCourses回调并将其设置为新值
  5. 提案

    如果您想像courseService.getCourses()中那样使用jasmine测试异步调用,则需要使用参数done通知您的测试。

    来自jasmine documentation - Asynchronous support

      

    在调用中调用done函数之前,此规范不会启动   到之前的每个。并且这个规范在完成之前不会完成   调用。

    正如您在文档中看到的那样,基本实现是:

    it("takes a long time", function(done) { //done as a parameter
          setTimeout(function() {     
            done();                           //done is resolve to finish your test
          }, 9000);
        });
    

    此外,您需要在收到异步回调后进行评估,因此请在then回调函数中引入它们。 因此,假设您的服务注入正确,您应该具有以下内容:

    it("get courses", function(done) { //Add done as parameter
            // spyOn(courseService, 'getCourses').and.callThrough();Now this spyOn makes no sense.
            deferredResolution.resolve();
            courseService.getCourses().then(function(result) {
              data = result;
              // expect(courseService.getCourses).toHaveBeenCalled(); Not needed
              // expect(data).toBeUndefined();  I assume you are expecting it defined
              // parentScope.$digest(); If you are only evaluating result and it's not change by the $scope, you don't need a $digest.
              expect(data).toBeDefined();
              done();
            }, function(){
               //If your async call fails and done() is never called, 
               // your test will fail with its timeout...
               //... Or you can force a test error
               expect(1).toBe(2);
               done();
            });
    
          });