Ładowanie pozorowanego pliku JSON w ramach testu Karma + AngularJS


85

Mam skonfigurowaną aplikację AngularJS z testami przy użyciu Karma + Jasmine. Mam funkcję, którą chcę przetestować, która pobiera duży obiekt JSON, konwertuje go na format, który jest bardziej użyteczny przez resztę aplikacji, i zwraca przekonwertowany obiekt. Otóż ​​to.

Do moich testów chciałbym mieć osobne pliki JSON (* .json) tylko z fałszywą zawartością JSON - bez skryptu. Na potrzeby testu chciałbym móc załadować plik JSON i wpompować obiekt do testowanej funkcji.

Wiem, że mogę osadzić JSON w fałszywej fabryce, jak opisano tutaj: http://dailyjs.com/2013/05/16/angularjs-5/ ale naprawdę chcę, aby JSON nie był zawarty w skrypcie - po prostu zwykły JSON akta.

Próbowałem kilku rzeczy, ale w tej dziedzinie jestem dość noobem. Najpierw skonfigurowałem Karmę tak, aby zawierała mój plik JSON, aby zobaczyć, co zrobi:

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

Spowodowało to:

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

Więc zmieniłem to tak, aby tylko obsługiwał pliki, a nie je „włączał”:

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

Teraz, gdy są tylko obsługiwane, pomyślałem, że spróbuję załadować plik przy użyciu $ http z mojej specyfikacji:

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

Kiedy uruchomiłem specyfikację, którą otrzymałem:

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

Co w moim rozumieniu oznacza, że ​​oczekuje fałszywej odpowiedzi od $ httpBackend. Więc ... w tym momencie nie wiedziałem, jak załadować plik za pomocą narzędzi Angular, więc pomyślałem, że spróbuję wypróbować jQuery, aby sprawdzić, czy przynajmniej uda mi się to uruchomić:

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

To skutkuje:

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

Sprawdzam tę prośbę w Charles i zwraca się do

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

Podczas gdy pozostałe pliki, które skonfigurowałem jako „dołączane” przez Karmę, są żądane pod adresem, na przykład:

/base/src/app.js

Najwyraźniej Karma tworzy jakiś katalog bazowy, z którego będą obsługiwane pliki. Więc dla kopnięć zmieniłem żądanie danych jQuery na

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

I to działa! Ale teraz czuję się brudna i muszę wziąć prysznic. Pomóż mi znów poczuć się czystym.

Odpowiedzi:


82

Używam konfiguracji kątowej z ziarnem kątowym. Skończyło się na tym, że rozwiązałem to za pomocą prostych plików .json i jasmine-jquery.js. Inni nawiązywali do tej odpowiedzi, ale zajęło mi trochę czasu, zanim wszystkie elementy znalazły się we właściwym miejscu. Mam nadzieję, że to pomoże komuś innemu.

Mam pliki json w folderze, /test/mocka moja aplikacja internetowa jest w /app.

my karma.conf.jsma następujące wpisy (między innymi):

basePath: '../',

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

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

wtedy mój plik testowy zawiera:

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

});

Prawdziwa sztuczka polegała na tym jasmine.getJSONFixtures().fixturesPath='base/test/mock'; , że pierwotnie go ustawiłem, test/mockale potrzebował tego base. Bez bazy mam takie błędy:

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
Cześć Cameron, pojawia się błąd: TypeError: Object # <Object> nie ma metody 'getJSONFixtures' ... czy dokonano zmiany w jaśminie czy coś?
— Melbourne2991

7
nie zdawałem sobie sprawy, że wymagałem jasmine-jquery ... zainstalowałem go i błąd zniknął
— Melbourne2991

To podejście wydaje się dość rozwlekłe i skomplikowane ... Nie wiem, czy te „urządzenia” zapewniają dodatkowe korzyści, ale poniższe podejście sprawdza się, jeśli chcesz tylko od czasu do czasu odczytać plik JSON.
— Septagram

43

Udostępnianie JSON przez urządzenie jest najłatwiejsze, ale z powodu naszej konfiguracji nie mogliśmy tego łatwo zrobić, więc napisałem alternatywną funkcję pomocniczą:

Magazyn

zainstalować

$ bower install karma-read-json --save

  OR

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

  OR

$ yarn add karma-read-json --dev

Stosowanie

  1. Umieść karma-read-json.js w swoich plikach Karma. Przykład:

    files = [
      ...
      'bower_components/karma-read-json/karma-read-json.js',
      ...
    ]
    
  2. Upewnij się, że Twój JSON jest obsługiwany przez Karmę. Przykład:

    files = [
      ...
      {pattern: 'json/**/*.json', included: false},
      ...
    ]
    
  3. Użyj tej readJSONfunkcji w swoich testach. Przykład:

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

1
karma-read-json to świetna opcja, jeśli nie możesz użyć lub zainstalować altany, której wymaga jasmine-jquery. (Polityka firmy Big-corp, w moim przypadku). Właśnie zainstalowałem przez npm ( npmjs.com/package/karma-read-json ) i działało świetnie.
— Melissa Avery-Weir

2
Myślę, że jest to najlepsze podejście, ponieważ nie wymaga uzależnienia od altany ... tylko karma / jasmine i karma-read-json. Byłem nawet w stanie to zrobić, biegając npm install karma-read-jsonzamiastbower install karma-read-json
— Eric Fuller

Świetna odpowiedź za podejście karmiczne - dzięki za jasny opis!
— nomadoj

A) trochę nowy na tej platformie, B) nie używam jquery podczas próby zbudowania modułu Angular. To działało dobrze dla mnie. moja konfiguracja zacisnęła się na karma.config.json -> pliki [] .. Nic nie dodałem w karma.config.json .. Ponadto: "const valid_respond = readJSON ('../ asset / organization.json'); „gdzie aktywa to standardowa lokalizacja aktywów.
— terary

10

Starałem się znaleźć rozwiązanie do ładowania danych zewnętrznych do moich przypadków testowych. Powyższy link: http://dailyjs.com/2013/05/16/angularjs-5/ Pracował dla mnie.

Kilka uwag:

"defaultJSON" musi być użyty jako klucz w twoim fałszywym pliku danych, to jest w porządku, ponieważ możesz po prostu odwołać się do defaultJSON.

mockedDashboardJSON.js:

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

Następnie w pliku testowym:

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

Mam wątpliwości… Czy będzie jeden moduł na plik JSON? ponieważ próbowałem dodać więcej niż jedną wartość do pojedynczych wartości, otrzymuję błąd nieznanego dostawcy dla drugiego json
— Pawan

6

wygląda na to, że Twoje rozwiązanie jest właściwe, ale są dwie rzeczy, których w nim nie lubię:

  • używa jaśminu
  • wymaga nowej krzywej uczenia się

właśnie natknąłem się na ten problem i musiałem go szybko rozwiązać, ponieważ nie miałem czasu na ostateczny termin, i zrobiłem następujące

mój zasób json był ogromny i nie mogłem go skopiować i wkleić go do testu, więc musiałem zachować osobny plik - ale zdecydowałem się zachować go jako javascript zamiast json, a potem po prostu zrobiłem:

var someUniqueName = ... the json ...

i włączyłem to do karmy conf obejmuje.

W razie potrzeby nadal mogę mockować odpowiedź http zaplecza.

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

Mógłbym również napisać nowy moduł kątowy, który zostanie dołączony tutaj, a następnie zmienić zasób json na coś podobnego

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

a następnie po prostu wstrzyknij SomeUniqueNamedo testu, który wygląda czyściej.

może nawet zapakuj go w usługę

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

to rozwiązanie było dla mnie szybsze, tak samo czyste i nie wymagało nowej krzywej uczenia się. więc wolę ten.


4

Szukałem tego samego. Spróbuję tego podejścia . Używa plików konfiguracyjnych, aby dołączyć pozorowane pliki danych, ale pliki są nieco większe niż json, ponieważ json musi zostać przekazany do angular.module ('MockDataModule'). Value, a następnie testy jednostkowe mogą również ładować wiele modułów a następnie ustawiona wartość jest dostępna do wstrzyknięcia do wywołania wstrzyknięcia beforeEach.

Znalazłem również inne podejście, które wygląda obiecująco w przypadku żądań xhr, które nie są kosztowne, to świetny post opisujący testy w połowie drogi, które, jeśli dobrze rozumiem, pozwalają kontrolerowi / usłudze faktycznie pobierać dane, jak w teście e2e, ale twój test w połowie ma rzeczywiste dostęp do zakresu kontrolera (e2e chyba nie).




1

Oto alternatywa dla odpowiedzi Camerona , bez potrzeby jasmine-jqueryani dodatkowej konfiguracji Karmy, aby przetestować np. Usługę Angular przy użyciu $resource:

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

I odpowiedni test jednostkowy:

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

Co to jest $ as in $ .when i $ .getJSON?
— Matt

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.