Carregando um arquivo JSON simulado no teste Karma + AngularJS

85

Eu tenho um aplicativo AngularJS configurado com testes usando Karma + Jasmine. Eu tenho uma função que quero testar que pega um objeto JSON grande, o converte em um formato que é mais consumível pelo resto do aplicativo e retorna esse objeto convertido. É isso aí.

Para meus testes, gostaria que você tivesse arquivos JSON separados (* .json) apenas com conteúdo JSON simulado - sem script. Para o teste, gostaria de poder carregar o arquivo JSON e bombear o objeto para a função que estou testando.

Eu sei que posso incorporar o JSON em uma fábrica de simulação, conforme descrito aqui: http://dailyjs.com/2013/05/16/angularjs-5/, mas eu realmente quero que o JSON não esteja contido no script - apenas JSON direto arquivos.

Eu tentei algumas coisas, mas sou bastante novato nesta área. Primeiro, configurei meu Karma para incluir meu arquivo JSON apenas para ver o que ele faria:

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

Isso resultou em:

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

Então eu mudei para apenas servir os arquivos e não "incluí-los":

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

Agora que eles estão apenas servidos, pensei em tentar carregar o arquivo usando $ http de dentro da minha especificação:

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

Quando executei a especificação, recebi:

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

O que, no meu entendimento, significa que espera uma resposta simulada de $ httpBackend. Então ... neste ponto eu não sabia como carregar o arquivo usando os utilitários Angular, então pensei em tentar o jQuery para ver se pelo menos conseguia fazer isso funcionar:

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

Isto resulta em:

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

Eu inspeciono esta solicitação em Charles e está fazendo uma solicitação para

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

Considerando que o resto dos arquivos que configurei para serem "incluídos" pelo Karma estão sendo solicitados em, por exemplo:

/base/src/app.js

Aparentemente, Karma está configurando algum tipo de diretório base para servir os arquivos. Então, para começar, alterei minha solicitação de dados jquery para

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

E funciona! Mas agora me sinto suja e preciso tomar um banho. Ajude-me a me sentir limpo novamente.

Aaronius
fonte

Respostas:

82

Estou usando uma configuração angular com semente angular. Acabei resolvendo isso com arquivos de fixação .json e jasmine-jquery.js. Outros fizeram alusão a essa resposta, mas demorei um pouco para colocar todas as peças no lugar certo. Espero que isto ajude alguém.

Tenho meus arquivos json em uma pasta /test/mocke meu webapp está em /app.

my karma.conf.jstem estas entradas (entre outras):

basePath: '../',

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

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

então meu arquivo de teste tem:

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

});

O verdadeiro truque era o que jasmine.getJSONFixtures().fixturesPath='base/test/mock'; eu tinha originalmente definido para apenas, test/mockmas precisava estar baselá. Sem a base, recebo erros como este:

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
fonte
1
Olá, Cameron, estou recebendo um erro: TypeError: Object # <Object> não tem o método 'getJSONFixtures' ... foi feita uma alteração no jasmim ou algo assim?
Melbourne2991
7
não percebi que precisava do jasmine-jquery ... instalei e o erro desapareceu
Melbourne2991
Essa abordagem parece bastante prolixa e complicada ... Não sei se esses "acessórios" oferecem benefícios adicionais, mas a abordagem abaixo vence se tudo o que você deseja é ler um arquivo JSON de vez em quando.
Septagrama
43

Servir JSON por meio do dispositivo de fixação é o mais fácil, mas devido à nossa configuração, não poderíamos fazer isso facilmente, então escrevi uma função auxiliar alternativa:

Repositório

Instalar

$ bower install karma-read-json --save

  OR

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

  OR

$ yarn add karma-read-json --dev

Uso

  1. Coloque karma-read-json.js em seus arquivos Karma. Exemplo:

    files = [
      ...
      'bower_components/karma-read-json/karma-read-json.js',
      ...
    ]
    
  2. Certifique-se de que seu JSON está sendo servido pelo Karma. Exemplo:

    files = [
      ...
      {pattern: 'json/**/*.json', included: false},
      ...
    ]
    
  3. Use a readJSONfunção em seus testes. Exemplo:

    var valid_respond = readJSON('json/foobar.json');
    $httpBackend.whenGET(/.*/).respond(valid_respond);
    
PizzaPanther
fonte
1
karma-read-json é uma ótima opção se você não consegue usar ou instalar o bower, que o jasmine-jquery requer. (Política de big-corp, no meu caso.) Acabei de instalar via npm ( npmjs.com/package/karma-read-json ) e funcionou muito bem.
Melissa Avery-Weir,
2
Acho que esta é a melhor abordagem, pois não requer uma dependência de caramanchão ... apenas karma / jasmim e karma-read-json. Eu fui capaz de fazer isso executando em npm install karma-read-jsonvez debower install karma-read-json
Eric Fuller
Ótima resposta para a abordagem do carma - obrigado pela descrição clara!
nomadoj
A) meio novo para esta plataforma, B) não usando jquery tentando construir um módulo angular. Isto funcionou bem para mim. minha configuração engasgou com os arquivos karma.config.json -> arquivos [] .. Não adicionei nada em karma.config.json .. Também: "const valid_respond = readJSON ('../ assets / organization.json'); "onde ativos é a localização padrão dos ativos.
terário
10

Tenho lutado para encontrar uma solução para carregar dados externos em meus casos de teste. O link acima: http://dailyjs.com/2013/05/16/angularjs-5/ Trabalhou para mim.

Algumas notas:

"defaultJSON" precisa ser usado como a chave em seu arquivo de dados fictício, isso é bom, pois você pode apenas referir-se a defaultJSON.

mockedDashboardJSON.js:

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

Então, em seu arquivo de teste:

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
fonte
Tenho uma dúvida .. Haverá um módulo por arquivo json? porque tentei adicionar mais de um valor em valores únicos, estou recebendo um erro de provedor desconhecido para o segundo json
Pawan
6

parece que sua solução é a certa, mas há 2 coisas que eu não gosto nela:

  • usa jasmim
  • requer nova curva de aprendizado

Acabei de encontrar esse problema e tive que resolvê-lo rapidamente, pois não tinha mais tempo para o prazo, e fiz o seguinte

meu recurso json era enorme e não consegui copiar e colar no teste, então tive que mantê-lo em um arquivo separado - mas decidi mantê-lo como javascript em vez de json, e então simplesmente fiz:

var someUniqueName = ... the json ...

e eu incluí isso no karma conf includes ..

eu ainda posso simular uma resposta HTTP de back-end, se necessário com ele.

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

eu também poderia escrever um novo módulo angular a ser incluído aqui e, em seguida, alterar o recurso json para algo como

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

e simplesmente injete SomeUniqueNameno teste, que parece mais limpo.

talvez até embrulhe-o em um serviço

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

essa solução era mais rápida para mim, tão limpa e não exigia nenhuma nova curva de aprendizado. então eu prefiro este.

cara mograbi
fonte
4

Eu estava procurando a mesma coisa. Vou tentar essa abordagem . Ele usa os arquivos de configuração para incluir os arquivos de dados fictícios, mas os arquivos são um pouco mais do que json, porque o json precisa ser passado para angular.module ('MockDataModule'). Value e então seus testes de unidade também podem carregar vários módulos e então o conjunto de valores está disponível para ser injetado na chamada beforeEach inject.

Também encontrei outra abordagem que parece promissora para solicitações xhr que não são caras, é um ótimo post que descreve o teste intermediário, que se bem entendi permite que seu controlador / serviço realmente recupere dados como em um teste e2e, mas seu teste intermediário tem acesso ao escopo do controlador (e2e não acho).

user1683523
fonte
1

Aqui está uma alternativa à resposta de Cameron , sem a necessidade de jasmine-jquerynenhuma configuração extra do Karma, para testar, por exemplo, um serviço Angular usando $resource:

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

E o teste de unidade correspondente:

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
fonte
O que é $ como em $ .when e $ .getJSON?
Matt