2014-02-02 3 views
0

Я пытаюсь проверить очень просто данные AngularJS служба загрузки JSON:

 
angular.module('archive', []) 
    .factory('Loader', function ($http) { 
    var result; 
    $http.get('path').success(function(data) { 
     result = data; 
     console.log('returning: ', result); 
    }); 
    return { 
     getData: result 
    } 
    }); 

Вот мой тест:

 
describe('TestArchive', function() { 
    beforeEach(module('archive')); 

    it('should load data', inject(function(Loader, $httpBackend){ 
    $httpBackend 
     .whenGET('path') 
     .respond(22); 
    var result = Loader.getData; 
    $httpBackend.flush(); 
    console.log(result); 
    })); 

}); 

я ожидал увидеть 22 загружен, но, как я вижу из console, этого не происходит, и result - undefined. Любая идея, что не так?

ответ

1

В определении услуг, вы в основном говорят:

var result; 
$http.get().success(function(data) { 
    result = data; // will be executed after below 'return' 
} 
return result; 

Это означает, результат будет неопределенным, когда вернулся из-за вызова асинхронной HTTP.

Лучше было бы вернуть функцию, которая возвращает фактический результат:

return { 
    getData: function() { return result; } 
}; 

Но следует помнить, что даже при таком подходе можно было бы назвать GetData() слишком рано (до HTTP запрос имел шанс закончить).

Наконец, безотказный способ просто вернуть обещание, созданные вручную (как это было предложено @dimirc), или просто обещание, возвращенное $ http.get() сам:

return { 
    getData: $http.get('path') 
}; 

Также следует знать, что ответить() любят данные ответа быть строкой, так как он может также принять статус HTTP (число) в качестве первых пар:

$httpBackend.whenGET('path').respond(200, '22'); 

он может работать с номером в качестве единственных пар, но всегда лучше четко указать свои намерения :)

0

Вы должны принять во внимание, что обратный вызов из .success называется асинхронно, поэтому мы можем использовать обещание лучше обработать результат.

angular.module('archive', []) 
    .factory('Loader', function ($http, $q) { 
    var result; 
    var deferred = $q.defer(); 
    $http.get('path').success(function(data) { 
     deferred.resolve(data); 
     console.log('returning: ', data); 
    }); 
    return { 
     getData: deferred.promise 
    } 
    }); 

И тест

describe('TestArchive', function() { 
    beforeEach(module('archive')); 

    it('should load data', inject(function(Loader, $httpBackend){ 
    $httpBackend 
     .whenGET('path') 
     .respond(22); 
    var promise = Loader.getData; 
    promise.then(function(result){ 
     console.log(result); 
    }) 
    $httpBackend.flush(); 
    })); 

}); 
+0

Hm ... таким образом 'Loader' возвращает обещание сделать мой API слишком сложным. Мне нужно, чтобы они вернули фактические данные. Я видел, что тот же трюк работает в Controllers - '$ scope.result = data' внутри обратного вызова '$ http(). success'. Я просто не могу заставить его работать с фабрикой. –

+0

Как-то я все еще не могу заставить его работать. Я не вижу 'console.log (result)' выполнен. Также я попытался поставить 'mydata = result' сразу после, но' mydata' остался 'undefined' –

0

предупреждение: Я не знаю, угловатые.

однако ...

angular.module('archive', []) 
    .factory('Loader', function ($http) { 
    return $http.get('path') 
     .then(function(result){ 
     console.log('returning: ', result); 
     return result; 
     }); 
}); 

и тест:

describe('TestArchive', function() { 
    beforeEach(module('archive')); 

    it('should load data', inject(function(Loader, $httpBackend){ 
    $httpBackend 
     .whenGET('path') 
     .respond(22); 
    var resultProm = Loader; 
    resultProm.then(function(result){ 
     console.log(result); 
     // maybe add an assertion here? 
    }); 
    $httpBackend.flush(); 
    })); 
}); 

Вы не сможете вернуть данные непосредственно из вашей директивы загрузчика, так что я не совсем уверен, этот тест имеет смысл ... Он должен назвать оба из console.log.

Похоже, что Angular попытался полностью избежать асинхронных тестов, поэтому он использует $httpBackend.flush как своего рода батут в тестах.

+0

на самом деле, хорошие шансы это не сработает ни из-за гарантий, предоставленных обещаниями. Я не мог найти много информации о $ q, за исключением того, что он вдохновлен Q ... В основном Angular не поддерживает асинхронные тесты, поэтому тестирование асинхронных вещей будет flaky ... –

+0

Просто протестирован, и он не регистрировался что-нибудь. Также проверено с утверждением 'data = result;', которое не было выполнено. Очень смущает. –

+0

, так что я не могу быть уверенным, что не выполняю этот материал в отладчике, но одна из гарантий с обещаниями заключается в том, что обратный вызов, зарегистрированный с помощью 'then', не будет вызываться до тех пор, пока текущий стек вызовов не будет очищен. Таким образом, эти функции нельзя было вызвать до тех пор, пока ваши (синхронные) тесты не будут завершены, что бесполезно для вас. Отсюда необходимость асинхронных тестов. Я не уверен, потому что я не знаю, как действует '$ q' ... –

Смежные вопросы