กำลังโหลดไฟล์ JSON จำลองภายในการทดสอบ Karma + AngularJS


85

ฉันมีแอป AngularJS ที่ตั้งค่าด้วยการทดสอบโดยใช้ Karma + Jasmine ฉันมีฟังก์ชั่นที่ฉันต้องการทดสอบที่ใช้ออบเจ็กต์ 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' }

ฉันตรวจสอบคำขอนี้ใน Charles และกำลังส่งคำขอไปที่

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

ในขณะที่ไฟล์อื่น ๆ ที่ฉันกำหนดค่าให้ "รวม" โดย Karma กำลังถูกร้องขอที่ตัวอย่างเช่น:

/base/src/app.js

เห็นได้ชัดว่า Karma ตั้งค่าไดเร็กทอรีฐานบางประเภทเพื่อให้บริการไฟล์จาก. ดังนั้นสำหรับการเตะฉันเปลี่ยนคำขอข้อมูล jquery เป็น

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

และได้ผล! แต่ตอนนี้ฉันรู้สึกสกปรกและต้องอาบน้ำ ช่วยให้ฉันรู้สึกสะอาดอีกครั้ง

คำตอบ:


82

ฉันใช้การตั้งค่าเชิงมุมกับเมล็ดเชิงมุม ฉันลงเอยด้วยการแก้ปัญหานี้ด้วยไฟล์ติดตั้ง. json และ jasmine-jquery.js คนอื่น ๆ พูดถึงคำตอบนี้ แต่ฉันต้องใช้เวลาสักพักเพื่อให้ชิ้นส่วนทั้งหมดอยู่ในตำแหน่งที่ถูกต้อง ฉันหวังว่านี่จะช่วยคนอื่นได้

ฉันมีไฟล์ json ในโฟลเดอร์/test/mockและเว็บแอปของฉันอยู่ใน/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'; ฉันตั้งไว้test/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

1
สวัสดีคาเมรอนฉันได้รับข้อผิดพลาด: TypeError: Object # <Object> ไม่มีเมธอด 'getJSONFixtures' ... มีการเปลี่ยนแปลงกับจัสมินหรืออะไร?
Melbourne2991

7
ไม่ทราบว่าฉันต้องการ jasmine-jquery ... ติดตั้งและข้อผิดพลาดหายไป
Melbourne2991

วิธีนี้ฟังดูละเอียดและซับซ้อน ... ฉันไม่รู้ว่า "ส่วนควบ" เหล่านี้ให้ประโยชน์เพิ่มเติมหรือไม่ แต่วิธีการด้านล่างนี้จะชนะได้หากสิ่งที่คุณต้องการคืออ่านไฟล์ JSON เป็นครั้งคราว
กันยายน

43

การให้บริการ JSON ผ่านฟิกซ์เจอร์เป็นวิธีที่ง่ายที่สุด แต่เนื่องจากการตั้งค่าของเราเราไม่สามารถทำได้อย่างง่ายดายดังนั้นฉันจึงเขียนฟังก์ชันตัวช่วยอื่น:

ที่เก็บ

ติดตั้ง

$ bower install karma-read-json --save

  OR

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

  OR

$ yarn add karma-read-json --dev

การใช้งาน

  1. ใส่arma-read-json.js ในไฟล์ Karma ของคุณ ตัวอย่าง:

    files = [
      ...
      'bower_components/karma-read-json/karma-read-json.js',
      ...
    ]
    
  2. ตรวจสอบให้แน่ใจว่า Karma ให้บริการ JSON ของคุณ ตัวอย่าง:

    files = [
      ...
      {pattern: 'json/**/*.json', included: false},
      ...
    ]
    
  3. ใช้readJSONฟังก์ชันในการทดสอบของคุณ ตัวอย่าง:

    var valid_respond = readJSON('json/foobar.json');
    $httpBackend.whenGET(/.*/).respond(valid_respond);
    

1
arma-read-json เป็นตัวเลือกที่ยอดเยี่ยมหากคุณไม่สามารถใช้หรือติดตั้ง bower ซึ่ง jasmine-jquery ต้องการ (นโยบายองค์กรใหญ่ในกรณีของฉัน) ฉันเพิ่งติดตั้งผ่าน npm ( npmjs.com/package/karma-read-json ) และใช้งานได้ดี
Melissa Avery-Weir

2
ฉันคิดว่านี่เป็นแนวทางที่ดีที่สุดเนื่องจากไม่ต้องการการพึ่งพา bower ... เพียงแค่กรรม / จัสมินและกรรมอ่าน json ฉันสามารถทำได้ด้วยการวิ่งnpm install karma-read-jsonแทนbower install karma-read-json
Eric Fuller

การตอบสนองที่ยอดเยี่ยมสำหรับแนวทางกรรม - ขอบคุณสำหรับคำอธิบายที่ชัดเจน!
nomadoj

A) ค่อนข้างใหม่สำหรับแพลตฟอร์มนี้ B) ไม่ได้ใช้ jquery เพื่อพยายามสร้าง Angular Module นี้ทำงานได้ดีสำหรับฉัน. การตั้งค่าของฉันสำลักในไฟล์arma.config.json -> [] .. ฉันไม่ได้เพิ่มอะไรในarma.config.json .. นอกจากนี้: "const valid_respond = readJSON ('../ assets / organization.json'); "โดยที่สินทรัพย์เป็นที่ตั้งมาตรฐานของสินทรัพย์
terary

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
    ....
}

ฉันมีข้อสงสัย .. จะมีหนึ่งโมดูลต่อไฟล์ json หรือไม่? สาเหตุที่ฉันพยายามเพิ่มมากกว่าหนึ่งค่าในค่าเดียวฉันได้รับข้อผิดพลาดของผู้ให้บริการที่ไม่รู้จักสำหรับ json ที่สอง
Pawan

6

ดูเหมือนว่าโซลูชันของคุณจะถูกต้อง แต่มี 2 สิ่งที่ฉันไม่ชอบ:

  • ใช้ดอกมะลิ
  • มันต้องมีช่วงการเรียนรู้ใหม่

ฉันเพิ่งพบปัญหานี้และต้องแก้ไขอย่างรวดเร็วเนื่องจากฉันไม่มีเวลาเหลือสำหรับกำหนดเส้นตายและฉันทำสิ่งต่อไปนี้

ทรัพยากร json ของฉันมีขนาดใหญ่มากและฉันไม่สามารถคัดลอกวางลงในการทดสอบได้ดังนั้นฉันจึงต้องเก็บมันไว้เป็นไฟล์แยกต่างหาก - แต่ฉันตัดสินใจที่จะเก็บไว้เป็น javascript แทนที่จะเป็น json จากนั้นฉันก็ทำ:

var someUniqueName = ... the json ...

และฉันรวมสิ่งนี้ไว้ในกรรม 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; 
})

วิธีแก้ปัญหานี้เร็วกว่าสำหรับฉันเช่นเดียวกับที่สะอาดและไม่ต้องการช่วงการเรียนรู้ใหม่ ๆ ฉันชอบอันนี้มากกว่า


4

ฉันกำลังมองหาสิ่งเดียวกัน ฉันจะลองใช้แนวทางนี้ มันใช้ไฟล์ config เพื่อรวมไฟล์ข้อมูลจำลอง แต่ไฟล์มีมากกว่า json เล็กน้อยเนื่องจาก json ต้องถูกส่งผ่านไปยัง angular.module ('MockDataModule') จากนั้นการทดสอบหน่วยของคุณยังสามารถโหลดหลายโมดูลได้ จากนั้นชุดค่าจะพร้อมที่จะฉีดเข้าไปใน beforeEach

นอกจากนี้ยังพบวิธีการอื่นที่ดูมีแนวโน้มสำหรับคำขอ xhr ที่ไม่เสียค่าใช้จ่ายมันเป็นโพสต์ที่ยอดเยี่ยมที่อธิบายการทดสอบกลางคันซึ่งถ้าฉันเข้าใจถูกต้องให้ตัวควบคุม / บริการของคุณดึงข้อมูลได้จริงเหมือนในการทดสอบ e2e แต่การทดสอบกลางคันของคุณมีจริง เข้าถึงขอบเขตคอนโทรลเลอร์ (ฉันไม่คิดว่า e2e)


2

มีตัวประมวลผลล่วงหน้า Karma ที่ทำงานกับไฟล์ JSON ด้วย มีอยู่ที่นี่:

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

และปลั๊กไร้ยางอายนี่คือสิ่งที่ฉันพัฒนาขึ้นซึ่งมีการรองรับ RequireJS

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



1

นี่คือทางเลือกสำหรับคำตอบของคาเมรอนโดยไม่จำเป็นต้องมีjasmine-jqueryหรือกำหนดค่า Karma เพิ่มเติมใด ๆ เพื่อทดสอบเช่นบริการเชิงมุมโดยใช้$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();
    });
});

$ ใน $ .when และ $ .getJSON คืออะไร?
Matt

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.