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

Aaronius
źródło

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
Cameron
źródło
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);
    
PizzaPanther
źródło
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
    ....
}
TOBlender
źródło
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.

facet mograbi
źródło
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).

user1683523
źródło
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();
    });
});
Lucas Cimon
źródło
Co to jest $ as in $ .when i $ .getJSON?
Matt