Chargement d'un fichier JSON simulé dans le test Karma + AngularJS

85

J'ai une application AngularJS configurée avec des tests utilisant Karma + Jasmine. J'ai une fonction que je veux tester qui prend un gros objet JSON, le convertit dans un format plus consommable par le reste de l'application et renvoie cet objet converti. C'est ça.

Pour mes tests, j'aimerais que vous ayez des fichiers JSON séparés (* .json) avec un contenu JSON simulé uniquement - pas de script. Pour le test, j'aimerais pouvoir charger le fichier JSON et pomper l'objet dans la fonction que je teste.

Je sais que je peux intégrer le JSON dans une usine fictive comme décrit ici: http://dailyjs.com/2013/05/16/angularjs-5/ mais je veux vraiment que le JSON ne soit pas contenu dans le script - juste du JSON direct des dossiers.

J'ai essayé quelques trucs mais je suis assez noob dans ce domaine. Tout d'abord, j'ai configuré mon Karma pour inclure mon fichier JSON juste pour voir ce qu'il ferait:

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

Cela a abouti à:

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

Alors je l'ai changé pour simplement servir les fichiers et ne pas les "inclure":

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

Maintenant qu'ils ne sont que servis, j'ai pensé essayer de charger le fichier en utilisant $ http dans mes spécifications:

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

Lorsque j'ai exécuté les spécifications, j'ai reçu:

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

Ce qui, à mon avis, signifie qu'il attend une réponse fictive de $ httpBackend. Donc ... à ce stade, je ne savais pas comment charger le fichier à l'aide des utilitaires Angular, alors j'ai pensé essayer jQuery pour voir si je pouvais au moins faire fonctionner cela:

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

Cela se traduit par:

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

J'inspecte cette demande dans Charles et il fait une demande à

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

Alors que le reste des fichiers que j'ai configurés pour être "inclus" par Karma sont demandés à, par exemple:

/base/src/app.js

Apparemment, Karma a mis en place une sorte de répertoire de base pour servir les fichiers. Donc, pour les coups de pied, j'ai changé ma demande de données jquery en

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

Et il fonctionne! Mais maintenant je me sens sale et j'ai besoin de prendre une douche. Aidez-moi à me sentir à nouveau propre.

Aaronius
la source

Réponses:

82

J'utilise une configuration angulaire avec une graine angulaire. J'ai fini par résoudre ce problème avec des fichiers de montage .json et jasmine-jquery.js. D'autres avaient fait allusion à cette réponse, mais il m'a fallu un certain temps pour mettre toutes les pièces au bon endroit. J'espère que ça aidera quelqu'un d'autre.

J'ai mes fichiers json dans un dossier /test/mocket ma webapp est là /app.

my karma.conf.jsa ces entrées (entre autres):

basePath: '../',

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

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

alors mon fichier de test a:

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

});

Le vrai truc était celui que jasmine.getJSONFixtures().fixturesPath='base/test/mock'; je l'avais réglé à l'origine, test/mockmais il avait besoin du basededans. Sans la base, j'ai eu des erreurs comme celle-ci:

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
la source
1
Bonjour Cameron, je reçois une erreur: TypeError: Object # <Object> n'a pas de méthode 'getJSONFixtures' ... un changement a-t-il été apporté à jasmine ou quelque chose?
Melbourne2991
7
Je n'ai pas réalisé que j'avais besoin de jasmine-jquery ... installé et l'erreur a disparu
Melbourne2991
Cette approche semble assez verbeuse et compliquée ... Je ne sais pas si ces "fixtures" offrent des avantages supplémentaires, mais l'approche ci-dessous l'emporte si tout ce que vous voulez est de lire un fichier JSON de temps en temps.
Septagram
43

Servir JSON via l'appareil est le plus simple, mais en raison de notre configuration, nous ne pouvions pas le faire facilement, j'ai donc écrit une fonction d'assistance alternative:

Dépôt

Installer

$ bower install karma-read-json --save

  OR

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

  OR

$ yarn add karma-read-json --dev

Usage

  1. Mettez karma-read-json.js dans vos fichiers Karma. Exemple:

    files = [
      ...
      'bower_components/karma-read-json/karma-read-json.js',
      ...
    ]
    
  2. Assurez-vous que votre JSON est servi par Karma. Exemple:

    files = [
      ...
      {pattern: 'json/**/*.json', included: false},
      ...
    ]
    
  3. Utilisez la readJSONfonction dans vos tests. Exemple:

    var valid_respond = readJSON('json/foobar.json');
    $httpBackend.whenGET(/.*/).respond(valid_respond);
    
PizzaPanther
la source
1
karma-read-json est une excellente option si vous ne pouvez pas utiliser ou installer bower, ce que jasmine-jquery requiert. (Politique Big-Corp, dans mon cas.) Je viens d'installer via npm ( npmjs.com/package/karma-read-json ) et cela a très bien fonctionné.
Melissa Avery-Weir
2
Je pense que c'est la meilleure approche car elle ne nécessite pas de dépendance à bower ... juste karma / jasmine et karma-read-json. J'ai même pu le faire en courant à la npm install karma-read-jsonplace debower install karma-read-json
Eric Fuller
Excellente réponse pour l'approche karma - merci pour la description claire!
nomadoj
A) un peu nouveau sur cette plate-forme, B) n'utilisant pas jquery pour essayer de construire un module angulaire. Cela a bien fonctionné pour moi. ma configuration s'est étouffée avec les fichiers karma.config.json -> [] .. Je n'ai rien ajouté dans karma.config.json .. Aussi: "const valid_respond = readJSON ('../ assets / Organizations.json'); "où les actifs est l'emplacement standard des actifs.
terary
10

J'ai eu du mal à trouver une solution pour charger des données externes dans mes cas de test. Le lien ci-dessus: http://dailyjs.com/2013/05/16/angularjs-5/ a travaillé pour moi.

Quelques notes:

"defaultJSON" doit être utilisé comme clé dans votre fichier de données fictif, c'est très bien, car vous pouvez simplement vous référer à defaultJSON.

mockedDashboardJSON.js:

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

Puis dans votre fichier de test:

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
la source
J'ai un doute .. Y aura-t-il un module par fichier json? parce que j'ai essayé d'ajouter plus d'une valeur sur des valeurs uniques, j'obtiens une erreur de fournisseur inconnue pour le deuxième json
Pawan
6

semble que votre solution est la bonne, mais il y a 2 choses que je n'aime pas à ce sujet:

  • il utilise du jasmin
  • cela nécessite une nouvelle courbe d'apprentissage

Je viens de rencontrer ce problème et j'ai dû le résoudre rapidement car je n'avais plus de temps pour la date limite, et j'ai fait ce qui suit

ma ressource json était énorme, et je ne pouvais pas la copier-coller dans le test, j'ai donc dû la conserver dans un fichier séparé - mais j'ai décidé de la conserver en javascript plutôt qu'en json, puis j'ai simplement fait:

var someUniqueName = ... the json ...

et j'ai inclus ceci dans karma conf includes ..

je peux toujours me moquer d'une réponse http backend si nécessaire avec elle.

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

je pourrais également écrire un nouveau module angulaire à inclure ici, puis changer la ressource json pour qu'elle soit quelque chose comme

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

puis injectez simplement SomeUniqueNamedans le test, qui semble plus propre.

peut-être même l'envelopper dans un service

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

cette solution était plus rapide pour moi, tout aussi propre et ne nécessitait aucune nouvelle courbe d'apprentissage. donc je préfère celui-ci.

mec mograbi
la source
4

Je cherchais la même chose. Je vais essayer cette approche . Il utilise les fichiers de configuration pour inclure les fichiers de données fictifs, mais les fichiers sont un peu plus que json, car le json doit être passé à angular.module ('MockDataModule'). Value et vos tests unitaires peuvent également charger plusieurs modules puis l'ensemble de valeurs est disponible pour être injecté dans l'appel d'injection beforeEach.

Nous avons également trouvé une autre approche qui semble prometteuse pour les demandes xhr qui ne sont pas coûteuses, c'est un excellent article qui décrit les tests à mi-parcours, qui, si je comprends bien, permet à votre contrôleur / service de récupérer des données comme dans un test e2e, mais votre test à mi-parcours a accès à la portée du contrôleur (e2e je ne pense pas).

user1683523
la source
1

Voici une alternative à la réponse de Cameron , sans avoir besoin jasmine-jqueryni de configuration supplémentaire de Karma, pour tester par exemple un service Angular en utilisant $resource:

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

Et le test unitaire correspondant:

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
la source
Qu'est-ce que $ comme dans $ .when et $ .getJSON?
Matt