在角度e2e测试中模拟$ httpBackend

时间:2013-05-29 06:40:43

标签: angularjs mocking acceptance-testing karma-runner end-to-end

有没有人知道如何在角度e2e测试中模拟$ httpBackend? 这个想法是在travis-ci上运行测试时存根XHR请求。 我正在使用业力来代理我在travis上运行的rails应用程序中的资产和部分资源。 我想在没有真正的数据库查询的情况下进行验收测试。

这是我的业力配置文件的一部分:

...
files = [
  MOCHA,
  MOCHA_ADAPTER,

  'spec/javascripts/support/angular-scenario.js',
  ANGULAR_SCENARIO_ADAPTER,

  'spec/javascripts/support/angular-mocks.js',
  'spec/javascripts/e2e/**/*_spec.*'
];
...

proxies = {
  '/app': 'http://localhost:3000/',
  '/assets': 'http://localhost:3000/assets/'
};
...

以下是我的spec文件的一部分:

beforeEach(inject(function($injector){
  browser().navigateTo('/app');
}));

it('should do smth', inject(function($rootScope, $injector){
  input('<model name>').enter('smth');
  //this is the point where I want to stub real http query
  pause();
}));

我试图通过$ injector接收$ httpBackend服务:

$injector.get('$httpBackend')

但这不是在我的测试运行的iframe中使用的那个。

我做的下一次尝试是使用angular.scenario.dsl,这里是代码samle:

angular.scenario.dsl('mockHttpGet', function(){
  return function(path, fakeResponse){
    return this.addFutureAction("Mocking response", function($window, $document, done) {
      // I have access to window and document instances 
      // from iframe where my tests run here
      var $httpBackend =  $document.injector().get(['$httpBackend']);
      $httpBackend.expectGET(path).respond(fakeResponse)
      done(null);
    });
  };
});

用法示例:

it('should do smth', inject(function($rootScope, $injector){
  mockHttpGet('<path>', { /* fake data */ });
  input('search.name').enter('mow');
  pause();
}));

这会导致以下错误:

<$httpBackend listing>  has no method 'expectGET'

所以,此时我不知道下一步。有没有人试过做这样的事情,这种类型的存根真的可能吗?

3 个答案:

答案 0 :(得分:7)

如果您真的想在E2E测试中模拟后端(这些测试称为场景,而Specs用于单元测试),那么这就是我在之前工作的项目中所做的。

我正在测试的应用程序名为studentsApp。这是一个通过查询REST api来搜索学生的应用程序。我想测试应用程序而不实际查询api。

我创建了一个名为studentsAppDev的E2E应用程序,我将studentsAppngMockE2E注入其中。在那里,我定义了mockBackend应该期望的调用以及要返回的数据。以下是我的studentsAppDev文件的示例:

"use strict";

// This application is to mock out the backend. 
var studentsAppDev = angular.module('studentsAppDev', ['studentsApp', 'ngMockE2E']);
studentsAppDev.run(function ($httpBackend) {

    // Allow all calls not to the API to pass through normally
    $httpBackend.whenGET('students/index.html').passThrough();

    var baseApiUrl = 'http://localhost:19357/api/v1/';
    var axelStudent = {
        Education: [{...}],
        Person: {...}
    };
    var femaleStudent = {
        Education: [{...}],
        Person: {...}
    };
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axe&')
        .respond([axelStudent, femaleStudent]);
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axel&')    
        .respond([axelStudent, femaleStudent]);
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axe&department=1&')
        .respond([axelStudent]);
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axe&department=2&')
        .respond([femaleStudent]);
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axe&department=3&')    
        .respond([]);

    ...

    $httpBackend.whenGET(baseApiUrl + 'departments/?teachingOnly=true')
        .respond([...]);
    $httpBackend.whenGET(baseApiUrl + 'majors?organization=RU').respond([...]);
});

然后,我在Jenkins CI服务器中迈出第一步,用studentsApp替换studentsAppDev,并在主index.html文件中添加对angular-mocks.js的引用。

答案 1 :(得分:1)

模拟后端是构建复杂的Angular应用程序的重要一步。它允许在不访问后端的情况下完成测试,您不会对事物进行两次测试,并且需要担心的依赖性较少。

Angular Multimocks是一种使用API​​的不同响应来测试应用行为的简单方法。

它允许您将不同场景的模拟API响应集定义为JSON文件。

它还允许您轻松更改方案。它允许这样做 你可以用不同的模拟文件组成“场景”。

如何将其添加到您的应用

将所需文件添加到您的页面后,只需将SELECT u.id, u.username, IFNULL(r.role, 'user') role, u.password FROM users AS u LEFT JOIN reps AS r ON u.id = r.user_id 作为依赖项添加到您的应用程序中:

scenario

将此添加到您的应用后,您就可以开始为API调用创建模拟。

让我们说您的应用程序进行以下API调用:

angular
  .module('yourAppNameHere', ['scenario'])
  // Your existing code here...

您可以创建$http.get('/games').then(function (response) { $scope.games = response.data.games; }); 模拟文件:

default

的示例
someGames.json

当您加载应用时,对{ "httpMethod": "GET", "statusCode": 200, "uri": "/games", "response": { "games": [{"name": "Legend of Zelda"}] } } 的来电将返回/games200

现在假设您要为同一API调用返回不同的响应,您可以通过更改URL将应用程序置于不同的方案中,例如{"games": [{"name": "Legend of Zelda"}]}

?scenario=no-games场景可以使用不同的模拟文件,比方说如下:

no-games

的示例
noGames.json

现在,当您加载应用时,对{ "httpMethod": "GET", "statusCode": 200, "uri": "/games", "response": { "games": [] } } 的来电将返回/games200

场景由清单中的各种JSON模拟组成,如下所示:

{"games": []}

然后,您可以排除模拟文件并在生产应用中删除{ "_default": [ "games/someGames.json" ], "no-games": [ "games/noGames.json" ] } 依赖项。

答案 2 :(得分:0)

这更像是单元/规格测试。一般来说,你应该在单元/规格测试中使用模拟而不是e2e /集成测试。基本上,将e2e测试视为对大多数集成的应用程序的期望......嘲弄某些事情会破坏e2e测试的目的。事实上,我不确定karam会如何将angular-mocks.js插入正在运行的应用程序中。

规范测试看起来像......

describe('Controller: MainCtrl', function () {
    'use strict';

    beforeEach(module('App.main-ctrl'));

    var MainCtrl,
        scope,
        $httpBackend;

    beforeEach(inject(function ($controller, $rootScope, $injector) {
        $httpBackend = $injector.get('$httpBackend');
        $httpBackend.when('GET', '/search/mow').respond([
            {}
        ]);
        scope = $rootScope.$new();
        MainCtrl = $controller('MainCtrl', {
            $scope: scope
        });
    }));

    afterEach(function () {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
    });

    it('should search for mow', function () {
        scope.search = 'mow';
        $httpBackend.flush();
        expect(scope.accounts.length).toBe(1);
    });
});