Laden einer nachgebildeten JSON-Datei innerhalb des Karma + AngularJS-Tests

84

Ich habe eine AngularJS-App mit Tests mit Karma + Jasmine eingerichtet. Ich möchte eine Funktion testen, die ein großes JSON-Objekt verwendet, es in ein Format konvertiert, das vom Rest der App mehr verwendet werden kann, und das konvertierte Objekt zurückgibt. Das ist es.

Für meine Tests möchte ich, dass Sie separate JSON-Dateien (* .json) haben, die nur falschen JSON-Inhalt enthalten - kein Skript. Für den Test möchte ich in der Lage sein, die JSON-Datei zu laden und das Objekt in die Funktion zu pumpen, die ich teste.

Ich weiß, dass ich den JSON wie hier beschrieben in eine Mock-Factory einbetten kann: http://dailyjs.com/2013/05/16/angularjs-5/, aber ich möchte wirklich, dass der JSON nicht im Skript enthalten ist - nur direkter JSON Dateien.

Ich habe ein paar Dinge ausprobiert, aber ich bin in diesem Bereich ziemlich noob. Zuerst habe ich mein Karma so eingerichtet, dass es meine JSON-Datei enthält, um zu sehen, was es tun würde:

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

Dies führte zu:

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

Also habe ich es geändert, um nur die Dateien bereitzustellen und sie nicht "einzuschließen":

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

Jetzt, da sie nur noch bereitgestellt werden, dachte ich, ich würde versuchen, die Datei mit $ http aus meiner Spezifikation heraus zu laden:

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

Als ich die Spezifikation ausführte, erhielt ich:

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

Was nach meinem Verständnis bedeutet, dass eine verspottete Antwort von $ httpBackend erwartet wird. Also ... zu diesem Zeitpunkt wusste ich nicht, wie ich die Datei mit Angular-Dienstprogrammen laden sollte, also dachte ich, ich würde jQuery ausprobieren, um zu sehen, ob ich das zumindest zum Laufen bringen könnte:

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

Das führt zu:

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

Ich inspiziere diese Anfrage in Charles und sie macht eine Anfrage an

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

Während der Rest der Dateien, die ich so konfiguriert habe, dass sie von Karma "eingeschlossen" werden, angefordert wird, zum Beispiel:

/base/src/app.js

Anscheinend richtet Karma eine Art Basisverzeichnis ein, aus dem die Dateien bereitgestellt werden. Also habe ich meine KQuery-Datenanforderung für Kicks in geändert

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

Und es funktioniert! Aber jetzt fühle ich mich schmutzig und muss duschen. Hilf mir, mich wieder sauber zu fühlen.

Aaronius
quelle

Antworten:

82

Ich verwende ein eckiges Setup mit eckigem Samen. Am Ende löste ich dies mit geraden .json-Fixture-Dateien und jasmine-jquery.js. Andere hatten auf diese Antwort angespielt, aber ich brauchte eine Weile, um alle Teile an den richtigen Ort zu bringen. Ich hoffe das hilft jemand anderem.

Ich habe meine JSON-Dateien in einem Ordner /test/mock und meine Webanwendung ist in/app .

Mein karma.conf.jshat diese Einträge (unter anderem):

basePath: '../',

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

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

dann hat meine Testdatei:

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);
});

});

Der eigentliche Trick war der, auf den jasmine.getJSONFixtures().fixturesPath='base/test/mock'; ich ihn ursprünglich eingestellt hatte, test/mockaber er brauchte den baseda drin . Ohne die Basis habe ich folgende Fehler erhalten:

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
Cameron
quelle
1
Hallo Cameron, ich erhalte eine Fehlermeldung: TypeError: Object # <Object> hat keine Methode 'getJSONFixtures' ... wurde eine Änderung an Jasmin vorgenommen oder so?
Melbourne2991
7
Ich wusste nicht, dass ich Jasmin-JQuery benötige ... habe es installiert und der Fehler ist verschwunden
Melbourne2991
Dieser Ansatz klingt ziemlich ausführlich und kompliziert ... Ich weiß nicht, ob diese "Geräte" zusätzliche Vorteile bieten, aber der folgende Ansatz gewinnt, wenn Sie nur von Zeit zu Zeit eine JSON-Datei lesen möchten.
Septagramm
43

JSON über das Fixture zu bedienen ist am einfachsten, aber aufgrund unseres Setups konnten wir das nicht einfach machen, also habe ich eine alternative Hilfsfunktion geschrieben:

Repository

Installieren

$ bower install karma-read-json --save

  OR

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

  OR

$ yarn add karma-read-json --dev

Verwendung

  1. Fügen Sie karma-read-json.js in Ihre Karma-Dateien ein. Beispiel:

    files = [
      ...
      'bower_components/karma-read-json/karma-read-json.js',
      ...
    ]
  2. Stellen Sie sicher, dass Ihr JSON von Karma bedient wird. Beispiel:

    files = [
      ...
      {pattern: 'json/**/*.json', included: false},
      ...
    ]
  3. Verwenden Sie die readJSONFunktion in Ihren Tests. Beispiel:

    var valid_respond = readJSON('json/foobar.json');
    $httpBackend.whenGET(/.*/).respond(valid_respond);
PizzaPanther
quelle
1
karma-read-json ist eine großartige Option, wenn Sie bower nicht verwenden oder installieren können, was für jasmine-jquery erforderlich ist. (Big-Corp-Richtlinie in meinem Fall.) Ich habe gerade über npm ( npmjs.com/package/karma-read-json ) installiert und es hat großartig funktioniert.
Melissa Avery-Weir
2
Ich denke, dies ist der beste Ansatz, da keine Abhängigkeit von der Laube erforderlich ist ... nur Karma / Jasmin und Karma-Read-Json. Ich konnte dies sogar tun, indem ich npm install karma-read-jsonstattbower install karma-read-json
Eric Fuller
Tolle Resonanz für den Karma-Ansatz - danke für die klare Beschreibung!
Nomadoj
A) ein bisschen neu auf dieser Plattform, B) keine jquery verwenden, um ein Angular-Modul zu erstellen. Das hat bei mir gut funktioniert. Mein Setup ist an der Datei karma.config.json -> files [] verstopft. Ich habe nichts in karma.config.json hinzugefügt. "wobei Vermögenswerte der Standardstandort von Vermögenswerten sind.
Terary
10

Ich habe Probleme, eine Lösung zum Laden externer Daten in meine Testfälle zu finden. Der obige Link: http://dailyjs.com/2013/05/16/angularjs-5/ Hat für mich funktioniert.

Einige Notizen:

"defaultJSON" muss als Schlüssel in Ihrer Scheindatendatei verwendet werden. Dies ist in Ordnung, da Sie nur auf defaultJSON verweisen können.

mockedDashboardJSON.js:

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

Dann in Ihrer Testdatei:

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
    ....
}
TOBlender
quelle
Ich habe Zweifel. Wird es ein Modul pro JSON-Datei geben? Da ich versucht habe, mehr als einen Wert zu einzelnen Werten hinzuzufügen, erhalte ich einen unbekannten Anbieterfehler für den zweiten json
Pawan
6

Sieht so aus, als wäre Ihre Lösung die richtige, aber es gibt zwei Dinge, die ich nicht mag:

  • es verwendet Jasmin
  • es erfordert eine neue Lernkurve

Ich bin gerade auf dieses Problem gestoßen und musste es schnell lösen, da ich keine Zeit mehr für die Frist hatte, und ich habe Folgendes getan

Meine JSON-Ressource war riesig und ich konnte sie nicht kopieren und in den Test einfügen, sodass ich sie in einer separaten Datei aufbewahren musste. Ich entschied mich jedoch dafür, sie als Javascript und nicht als JSON zu speichern, und dann tat ich einfach:

var someUniqueName = ... the json ...

und ich habe dies in Karma Conf aufgenommen.

Ich kann immer noch eine Backend-http-Antwort verspotten, wenn nötig.

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

Ich könnte auch ein neues Winkelmodul schreiben, das hier enthalten sein soll, und dann die JSON-Ressource so ändern, dass sie so etwas wie ist

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

und dann einfach SomeUniqueNamein den Test injizieren , der sauberer aussieht.

vielleicht sogar in einen Dienst einwickeln

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

Diese Lösung war für mich schneller, genauso sauber und erforderte keine neue Lernkurve. also bevorzuge ich diesen.

Kerl Mograbi
quelle
4

Ich habe das Gleiche gesucht. Ich werde diesen Ansatz versuchen . Es verwendet die Konfigurationsdateien, um die Scheindatendateien einzuschließen, aber die Dateien sind etwas mehr als json, da der json an angular.module ('MockDataModule'). Value übergeben werden muss und Ihre Unit-Tests dann auch mehrere Module laden können und dann steht der eingestellte Wert zur Verfügung, um in den Aufruf von beforeEach injiziert zu werden.

Es wurde auch ein anderer Ansatz gefunden , der für xhr-Anfragen, die nicht teuer sind, vielversprechend aussieht. Es ist ein großartiger Beitrag, der Midway-Tests beschreibt. Wenn ich das richtig verstehe, kann Ihr Controller / Service tatsächlich Daten wie bei einem e2e-Test abrufen, aber Ihr Midway-Test hat tatsächlich Zugriff auf den Controller-Bereich (e2e glaube ich nicht).

user1683523
quelle
1

Hier ist eine Alternative zu Camerons Antwort, ohne dass jasmine-jqueryeine zusätzliche Karma-Konfiguration erforderlich ist, um z. B. einen Angular-Dienst zu testen, indem Sie $resource:

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

Und der entsprechende Unit Test:

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();
    });
});
Lucas Cimon
quelle
Was ist $ wie in $ .when und $ .getJSON?
Matt