web-dev-qa-db-ja.com

AngularJS単体テストで$ window.location.replaceをモックする方法は?

次のサービスを利用できます。

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

単体テストで_$window_オブジェクトをモックして、テストの実行時にページの再読み込みを防ぐ方法は?

使ってみた

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

、しかしそれは機能しませんでした(まだ_"Some of your tests did a full page reload!"_エラーが発生しました)そして

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

、しかし、スタックトレースがangular自分のソースのみを指しているため、エラー(_Error: [ng:areq] Argument 'fn' is not a function, got Object_)を受け取っていたので、あまり役に立ちませんでした...

40
szimek

Chrome(他のブラウザではテストしなかった)では、location.replaceは読み取り専用であるため、spyOnはこれを置き換えることができませんでした。

$provide.value動作するはずです。コードのどこかが間違っているに違いありません。

これが作業ユニットテストです

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');
    });
});
58
LostInComputer

簡単ですが、おそらくエレガントではないソリューションを使用します。私は$ window.locationのラッパーを書いていますが、これをモックできます。それをあなたのコードに関連付けて、$ windowをモックするのではなく、whatever.redirect関数をモックします(ここでは、実際の関数はより複雑であると想定しています)。

だから私は最終的に:

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

その後、リダイレクトメソッドを直接モックできます。コードが1行しかないため、実際に問題が発生することはありません。

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

これは私の目的には十分であり、呼び出されたURLを検証できます。

6
PaulL

私はあなたのために働くかもしれない別のアプローチを提供します。最終的にユーザーをリダイレクトするコントローラー「アクション」のユニットテスト中に同じ問題に直面しました(フルページ読み込みですが、より大きなWebサイト/アプリケーションの別のページに)。コンテキストを与えるために、コントローラーはAJAXリクエストを発行します。レスポンスがOKの場合、$ 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
Shawn Flahave
$location.path('/someNewPath');

$ location.replace(); //または、これらを次のように連鎖できます:$ location.path( '/ someNewPath')。replace();

0
Kevin Barasa

$window.locationを呼び出すのではなく、 $ location サービスを使用したいと思います。この機能を説明するページ全体もここにあります: http://docs.angularjs.org/guide/dev_guide.services.$location

これを使用すると、テストで$ locationサービスのスタブバージョンを使用するのはかなり簡単になります。

0
calamari