85

我有一个使用 Karma+Jasmine 进行测试的 AngularJS 应用程序。我有一个要测试的函数,它需要一个大型 JSON 对象,将其转换为应用程序其余部分更易于使用的格式,然后返回该转换后的对象。而已。

对于我的测试,我希望你有单独的 JSON 文件 (*.json),只有模拟 JSON 内容——没有脚本。对于测试,我希望能够加载 JSON 文件并将对象泵入我正在测试的函数中。

我知道我可以将 JSON 嵌入到模拟工厂中,如下所述:http: //dailyjs.com/2013/05/16/angularjs-5/但我真的希望 JSON 不包含在脚本中——只是直接的 JSON文件。

我已经尝试了一些东西,但我在这方面相当菜鸟。首先,我设置了我的 Karma 以包含我的 JSON 文件,只是为了看看它会做什么:

files = [
    ...
    'mock-data/**/*.json'
    ...
]

这导致:

Chrome 27.0 (Mac) ERROR
Uncaught SyntaxError: Unexpected token :
at /Users/aaron/p4workspace4/depot/sitecatalyst/branches/anomaly_detection/client/anomaly-detection/mock-data/two-metrics-with-anomalies.json:2

因此,我将其更改为仅提供文件而不是“包含”它们:

files = [
    ...
    { pattern: 'mock-data/**/*.json', included: false }
    ...
]

现在它们只提供服务,我想我会尝试使用我的规范中的 $http 加载文件:

$http('mock-data/two-metrics-with-anomalies.json')

当我运行我收到的规范时:

Error: Unexpected request: GET mock-data/two-metrics-with-anomalies.json

在我看来,这意味着它期望来自 $httpBackend 的模拟响应。所以......此时我不知道如何使用 Angular 实用程序加载文件,所以我想我会尝试 jQuery 看看我是否至少可以让它工作:

$.getJSON('mock-data/two-metrics-with-anomalies.json').done(function(data) {
    console.log(data);
}).fail(function(response) {
    console.log(response);
});

这导致:

Chrome 27.0 (Mac) LOG: { readyState: 4,
responseText: 'NOT FOUND',
status: 404,
statusText: 'Not Found' }

我在查尔斯检查了这个请求,它正在请求

/mock-data/two-metrics-with-anomalies.json

而我已配置为由 Karma “包含”的其余文件正在被请求,例如:

/base/src/app.js

显然 Karma 设置了某种基本目录来提供文件。所以为了踢球,我将我的 jquery 数据请求更改为

$.getJSON('base/mock-data/two-metrics-with-anomalies.json')...

它有效!但现在我觉得很脏,需要洗个澡。帮助我再次感到干净。

4

8 回答 8

82

我正在使用带有角种子的角设置。我最终用直接的 .json 固定文件和 jasmine-jquery.js 解决了这个问题。其他人曾提到过这个答案,但我花了一段时间才把所有的部分都放在正确的位置。我希望这对其他人有帮助。

我的 json 文件在一个文件夹中/test/mock,我的 webapp 在/app.

karma.conf.js有这些条目(除其他外):

basePath: '../',

files: [
      ... 
      'test/vendor/jasmine-jquery.js',
      'test/unit/**/*.js',

      // fixtures
      {pattern: 'test/mock/*.json', watched: true, served: true, included: false}
    ],

然后我的测试文件有:

describe('JobsCtrl', function(){
var $httpBackend, createController, scope;

beforeEach(inject(function ($injector, $rootScope, $controller) {

    $httpBackend = $injector.get('$httpBackend');
    jasmine.getJSONFixtures().fixturesPath='base/test/mock';

    $httpBackend.whenGET('http://blahblahurl/resultset/').respond(
        getJSONFixture('test_resultset_list.json')
    );

    scope = $rootScope.$new();
    $controller('JobsCtrl', {'$scope': scope});

}));


it('should have some resultsets', function() {
    $httpBackend.flush();
    expect(scope.result_sets.length).toBe(59);
});

});

真正的诀窍是jasmine.getJSONFixtures().fixturesPath='base/test/mock'; 我最初将它设置为 justtest/mock但它需要base在那里。没有基础,我得到这样的错误:

Error: JSONFixture could not be loaded: /test/mock/test_resultset_list.json (status: error, message: undefined)
at /Users/camd/gitspace/treeherder-ui/webapp/test/vendor/jasmine-jquery.js:295
于 2013-08-30T01:44:39.673 回答
43

通过夹具提供 JSON 是最简单的,但由于我们的设置我们不能轻易做到这一点,所以我写了一个替代的辅助函数:

存储库

安装

$ bower install karma-read-json --save

  OR

$ npm install karma-read-json --save-dev

  OR

$ yarn add karma-read-json --dev

用法

  1. 将 karma-read-json.js 放入您的 Karma 文件中。例子:

    files = [
      ...
      'bower_components/karma-read-json/karma-read-json.js',
      ...
    ]
    
  2. 确保您的 JSON 由 Karma 提供服务。例子:

    files = [
      ...
      {pattern: 'json/**/*.json', included: false},
      ...
    ]
    
  3. 在测试中使用该readJSON功能。例子:

    var valid_respond = readJSON('json/foobar.json');
    $httpBackend.whenGET(/.*/).respond(valid_respond);
    
于 2015-01-07T23:30:51.623 回答
10

我一直在努力寻找将外部数据加载到我的测试用例中的解决方案。上面的链接:http : //dailyjs.com/2013/05/16/angularjs-5/ 为我工作。

一些注意事项:

“defaultJSON”需要用作模拟数据文件中的键,这很好,因为您可以参考 defaultJSON。

模拟仪表板JSON.js:

'use strict'
angular.module('mockedDashboardJSON',[])
.value('defaultJSON',{
    fakeData1:{'really':'fake2'},
    fakeData2:{'history':'faked'}
});

然后在您的测试文件中:

beforeEach(module('yourApp','mockedDashboardJSON'));
var YourControlNameCtrl, scope, $httpBackend, mockedDashboardJSON;
beforeEach(function(_$httpBackend_,defaultJSON){
    $httpBackend.when('GET','yourAPI/call/here').respond(defaultJSON.fakeData1);
    //Your controller setup 
    ....
});

it('should test my fake stuff',function(){
    $httpBackend.flush();
    //your test expectation stuff here
    ....
}
于 2013-08-16T15:34:24.333 回答
6

看起来您的解决方案是正确的,但有两件事我不喜欢它:

  • 它使用茉莉花
  • 它需要新的学习曲线

我刚刚遇到了这个问题,因为我没有时间离开截止日期,所以我不得不迅速解决它,我做了以下事情

我的 json 资源很大,我无法将其复制粘贴到测试中,所以我不得不将它保存为一个单独的文件 - 但我决定将其保留为 javascript 而不是 json,然后我就这样做了:

var someUniqueName = ... the json ...

我将它包含在 karma conf 中,包括..

如果需要,我仍然可以模拟后端 http 响应。

$httpBackend.whenGET('/some/path').respond(someUniqueName);

我还可以编写一个新的角度模块来包含在此处,然后将 json 资源更改为类似

angular.module('hugeJsonResource', []).constant('SomeUniqueName', ... the json ... );

然后简单地注入SomeUniqueName测试,看起来更干净。

甚至可能将其包装在服务中

angular.module('allTestResources',[]).service('AllTestResources', function AllTestResources( SomeUniqueName , SomeOtherUniqueName, ... ){
   this.resource1 = SomeUniqueName;
   this.resource2 = SomeOtherUniqueName; 
})

这个解决方案对我来说更快,同样干净,并且不需要任何新的学习曲线。所以我更喜欢这个。

于 2014-10-02T21:39:09.357 回答
3

我一直在寻找同样的东西。我要试试这种方法。它使用配置文件来包含模拟数据文件,但文件比 json 多一点,因为 json 需要传递给 angular.module('MockDataModule').value 然后你的单元测试也可以加载多个模块然后可以将值集注入到 beforeEach 注入调用中。

还发现了另一种看起来很有希望的 xhr 请求但成本不高的方法,这是一篇描述中途测试的好文章,如果我理解正确的话,它可以让您的控制器/服务像在 e2e 测试中一样实际检索数据,但是您的中途测试具有实际访问控制器范围(我不认为是 e2e)。

于 2013-07-11T19:56:16.440 回答
2

还有一些 Karma 预处理器也可以处理 JSON 文件。这里有一个:

https://www.npmjs.org/package/karma-ng-json2js-preprocessor

还有不要脸的插件,这是我开发的,有RequireJS支持

https://www.npmjs.org/package/karma-ng-json2js-preprocessor-requirejs

于 2014-05-13T19:49:25.870 回答
1

您可以使用karma-html2js-preprocessor将 json 文件添加到 __html__ 全局。

有关详细信息,请参阅此答案:https ://stackoverflow.com/a/22103160/439021

于 2014-03-01T08:53:03.423 回答
1

这是Cameron的答案的替代方案,无需jasmine-jquery任何额外的 Karma 配置,即可使用以下方法测试 Angular 服务$resource

angular.module('myApp').factory('MyService', function ($resource) {
    var Service = $resource('/:user/resultset');
    return {
        getResultSet: function (user) {
            return Service.get({user: user}).$promise;
        }
    };
});

以及相应的单元测试:

describe('MyServiceTest', function(){
    var $httpBackend, MyService, testResultSet, otherTestData ;

    beforeEach(function (done) {
        module('myApp');
        inject(function ($injector) {
            $httpBackend = $injector.get('$httpBackend');
            MyService = $injector.get('MyService');
        });
        // Loading fixtures
        $.when(
            $.getJSON('base/test/mock/test_resultset.json', function (data) { testResultSet = data; }),
            $.getJSON('base/test/mock/test_other_data.json', function (data) { otherTestData = data; })
        ).then(done);
    });

    it('should have some resultset', function() {
        $httpBackend.expectGET('/blahblahurl/resultset').respond(testResultSet);
        MyService.getResultSet('blahblahurl').then(function (resultSet) {
            expect(resultSet.length).toBe(59);
        });
        $httpBackend.flush();
    });
});
于 2015-07-15T13:35:46.230 回答