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.

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

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

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

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.


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




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

O que é $ como em $ .when e $ .getJSON?
— Matt

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.