Как обмануть $window.location.replace в AngularJS unit test?

У меня есть следующая услуга:

angular.module("services")
.factory("whatever", function($window) {
  return {
    redirect: function() {
      $window.location.replace("http://www.whatever.com");
    }
  };
});

Как обмануть объект $window в unit test, чтобы предотвратить перезагрузку страницы при выполнении тестов?

Я попытался использовать

spyOn($window.location, 'replace').andReturn(true);

но он не работает (по-прежнему получена ошибка "Some of your tests did a full page reload!") и

$provide.value('$window', {location: {replace: jasmine.createSpy()}})

но я получал ошибку (Error: [ng:areq] Argument 'fn' is not a function, got Object) с трассировкой стека, указывающей только на angular собственный источник, поэтому он не очень помог...

+39
27 нояб. '13 в 20:21
источник поделиться
5 ответов

В Chrome (не тестировалось в других браузерах) location.replace читается только так, что spyOn не смог его заменить.

$provide.value должен работать. Что-то должно быть не так где-то в вашем коде.

Вот работающий unit test

describe('whatever', function() {
    var $window, whatever;

    beforeEach(module('services'));

    beforeEach(function() {
      $window = {location: { replace: jasmine.createSpy()} };

      module(function($provide) {
        $provide.value('$window', $window);
      });

      inject(function($injector) {
        whatever = $injector.get('whatever');
      });
    });

    it('replace redirects to http://www.whatever.com', function() {
      whatever.redirect();
      expect($window.location.replace).toHaveBeenCalledWith('http://www.whatever.com');
    });
});
+54
02 янв. '14 в 15:28
источник

Я собираюсь с более легким, но, возможно, менее элегантным решением. Я пишу обертку для $window.location, которую я могу затем высмеять. Относясь к вашему коду, я бы издевался над функцией any.redirect, а не издеваясь над $window (я предполагаю, что ваша реальная функция сложнее).

Итак, я получаю:

angular.module("services")
.factory("whatever", function($window) {
  return {
    do_stuff_that_redirects: function() {
      lots of code;
      this.redirect("http://www.whatever.com");
      maybe_more_code_maybe_not;
    },

    redirect: function(url) {
      $window.location.replace(url);
    }
  };
});

Затем я могу непосредственно высмеять метод перенаправления и просто верю, что поскольку это только одна строка кода, это не может пойти не так.

spyOn(whatever, 'redirect').andCallFake(function(){});
expect(whatever.redirect).toHaveBeenCalledWith('http:/my.expected/url');

Этого достаточно для моих целей и позволяет проверять URL-адрес.

+6
13 апр. '14 в 9:40
источник

Я предлагаю другой подход, который может сработать для вас. Я столкнулся с такой же проблемой, пока модуль тестировал "действие" контроллера, которое в конечном итоге перенаправляет пользователя (полностраничная загрузка, но на другую страницу на более крупном веб-сайте/приложении). Чтобы дать какой-то контекст, контроллер запускает запрос AJAX, и если ответ в порядке, он перенаправляет пользователя на другую страницу через $window.location.replace():

$http.post('save', data)
        .success(function(responseData, status, headers, config) {
            if(responseData.redirect) {
                $window.location.replace(responseData.redirect);
            }
        })
        .error(function(responseData, status, headers, config) {
             console.error("ERROR while trying to create the Event!!");
        });

Тест для этой функции контроллера вызвал то же самое: "Некоторые из ваших тестов сделали полную перезагрузку страницы!" ошибка. Поэтому я добавил следующее к функции beforeEach() для спецификации контроллера, чтобы изрубить службу $window:

mockWindow = { location: { replace: function(url) { console.log('redirecting to: ' + url); } } };
eventCtrl = $controller('EventCtrl', { $scope: scope, $window: mockWindow });

Конечно, это решение мешает мне (чисто) проверять, что функция replace была вызвана с ожидаемым аргументом, но мне сейчас это не очень важно... Надеюсь, что это поможет.

+4
31 янв. '14 в 23:15
источник

Я думаю, что вы хотите использовать службу $location, а не вызов $window.location. Существует также целая страница, объясняющая эту функцию здесь: http://docs.angularjs.org/guide/dev_guide.services.$location.

Используя это, должно быть довольно просто использовать в тестах пропущенную версию службы определения местоположения $.

0
27 нояб. '13 в 20:34
источник
$location.path('/someNewPath');

$location.replace(); // или вы можете связать их как: $location.path('/someNewPath'). replace();

0
06 апр. '17 в 6:08
источник

Посмотрите другие вопросы по меткам или Задайте вопрос