基于Jasmine 的 Javascript 测试 -- 第六章
发布在基于Jasmine的Javascript测试2013年10月14日view:5663
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

第六章 Spies


正如我们前面所学的,Jasmine让我们可以测试代码是否按照我们想要的方式去工作。但是我们还想要检查函数是否被调用,以及它们是否按照我们所想的方式被调用。即我们明确的指出我们的代码应该怎样工作。

在Jasmine中,spy 名符其实:它让我们可以对程序进行监视(总的来说,它完成的不仅仅是变量检查这么简单)。虽然可能没有詹姆斯邦德那么厉害,但是也是个不错的间谍。

基本用法: 监视一个函数

监视行为让我们可以把程序的一部分替换为一个spy。一个spy可以把自己伪装成一个函数或者对象。它在什么时候能发挥作用呢?

假设你有一个叫做Dictionary的类。它用来表示一个英语词典,然后返回 “hello” 和 “world”。

 var Dictionary = function() {};
 Dictionary.prototype.hello = function() {
 return "hello";
 };
   Dictionary.prototype.world = function() {
   return "world";
 };

现在让我们假设你还有一个叫做Person的类,可以将Dictionary作为输入参数然后输出 “hello world”。它可能是像下面这样工作的:

 var Person = function() {};
       Person.prototype.sayHelloWorld = function(dict) {
       return dict.hello() + " " + dict.world();
 };

为了让你的 Person 返回 “hello world”。你会像下面这样做:

 var dictionary = new Dictionary;
 var person = new Person;
 person.sayHelloWorld(dictionary); // 返回 "hello world"

理论上,你可以让sayHelloWorld这个函数返回一个单纯的叫做”hello world“的字符串。但是你并不像这么做--你想要确保你的Person咨询了Dictionary。

Spy将会帮助你达成心愿;你可以监视字典确保它被person使用。代码如下所示:

 describe("Person", function() {
  it('uses the dictionary to say "hello world"', function() {
   var dictionary = new Dictionary;
   var person = new Person;
  spyOn(dictionary, "hello"); // 用一个spy替换hello函数
  spyOn(dictionary, "world"); //用另一个函数替换world函数
  person.sayHelloWorld(dictionary);
  expect(dictionary.hello).toHaveBeenCalled(); // 没有第一个spy不可能成功
  expect(dictionary.world).toHaveBeenCalled(); // 没有第二个spy不可能成功
   });
       });

让我们来回顾以上代码。首先,我们实例化了两个对象。接着,我们监视了dictionary的 hello 和 world 方法。这告诉Jasmine用两个spy替换了dictionary的 hello 和 world 方法。然后我们调用person的 sayHelloWorld 函数来确保字典的方法被调用。

将上述代码在一个spec中运行将会给你一个肯定的结果:你的程序确实调用了字典的两个方法。

为什么它是有用的?现在讲你的英文字典改成西班牙语字典试试看:

  var Dictionary = function() {};
    Dictionary.prototype.hello = function() {
  return "hola";
   };
   Dictionary.prototype.world = function() {
  return "mundo";
     };

尽管 sayHelloWorld 函数返回了不同的结果 ,但是spec依然能够运行成功,因为无论你说的是英语还是西班牙语,字典的两个方法都被调用了。

OK,可能这并不是你所想要的。也许你只想确保你的 person 的 sayHelloWorld 函数调用了一个指定的字典。Jasmine完全可以做到。来看一看下面的例子:

      describe("Person", function() {
            it('uses the dictionary to say "hello world"', function() {
            var dictionary = new Dictionary;
            var person = new Person;
           spyOn(person, "sayHelloWorld"); // 用一个spy替换了sayHelloWorld函数
           person.sayHelloWorld(dictionary);
           expect(person.sayHelloWorld).toHaveBeenCalledWith(dictionary);
        });
      });

正如你所读到的(Jasmine看起来完全就像是英语!)。上面的spy确保了sayHelloWorld的参数是这个dictionary而不是别的dictionary。在Jasmine中运行,它会告诉你一切正常。

如果你想要陀一些东邪没有被调用。这和你想让某些变量没有被使用的方法一样:使用 .not。那么,例如你想确保某个函数没有调用特定的参数,你可以像这样写:

      expect(person.sayHelloWorld).not.toHaveBeenCalledWith(dictionary);

Calling Through: 让你的Spy更强

简单的使用spyOn可以让spy函数知道哪些东西被调用。

如果你想要监视一个函数并且确保它正常工作,你可以使用 call through。这让你的spy更加狡猾。你需要做的仅仅是将 addCallThrough 添加到 spyOncall 上面去:

     describe("Person", function() {
    it('uses the dictionary to say "hello world"', function() {
   var dictionary = new Dictionary;
    var person = new Person;
   spyOn(dictionary, "hello"); // 用一个spy替换了hello函数
   spyOn(dictionary, "world"); // 用另一个spy替换了world函数
      var result = person.sayHelloWorld(dictionary);
   expect(result).toEqual("hello world"); //没有callThrough不可能运行成功
     expect(dictionary.hello).toHaveBeenCalled();
     expect(dictionary.world).toHaveBeenCalled();
   });
      });

这里的spy函数会完成之前的spy函数所完成的工作,但是它是一个更好的spy可以让你观察到它的内部活动。

确保一个spy返回一个具体值

你需要确保一个spy总是返回一个具体数值。让我你们假设你想让字典的 hello 方法的 spy 说法语:

  it("can give a Spanish hello", function() {
 var dictionary = new Dictionary;
  var person = new Person;
 spyOn(dictionary, "hello").andReturn("bonjour"); //注意到这里新的替换
  var result = person.sayHelloWorld(dictionary);
    expect(result).toEqual("bonjour world");
     });

当你想确保即使函数发生了变化所有的一切还能正常运行时,这相当有用。你也可以借此来查看当返回一个错误的输出时程序怎么反应。在这个例子中,字典的hello方法就会失效:

用一个完全不同的spy替换函数

Spies可以更疯狂。他们能够去调用一个伪函数,例如:

 it("can call a fake function", function() {
 var fakeHello = function() {
 alert("I am a spy! Ha ha!");
  return "hello";
  };
     var dictionary = new Dictionary();
     spyOn(dictionary, "hello").andCallFake(fakeHello);
     dictionary.hello(); // 弹出一个警告框
           });

这意味着你能针对一个有bug的API对代码进行测试。

创造一个新的Spy函数

在前面的例子中,我们用spy替换了已经存在的函数。但有时为那些完全不存在的函数创建一个spy也是很有用的。乳如果你想给你的Person一个叫做getName的spy,你可以创造一个全新的spy。

和spyOn通过”吞下“一个已经存在的函数来创建一个spy不同,Jasmine。createSpy并不需要一个已经存在的函数。它能自己创造一个全新的函数:

 it("can have a spy function", function() {
 var person = new Person();
 person.getName = jasmine.createSpy("Name spy");
person.getName();
expect(person.getName).toHaveBeenCalled();
});:

看看其他的spy,由Jasmine.createSpy创造的spy可以拥有其他与之捆绑的方法。

 person.getSecretAgentName = jasmine.createSpy("Name spy").andReturn("James
 Bond");
 person.getRealName = jasmine.createSpy("Name spy 2").andCallFake(function() {
 alert("I am also a spy! Ha ha!");
 return "Evan Hahn";
 });

创建一个全新的Spy对象

相比创建一个新的spy函数,你还可以创建一个新的spy对象:

 var tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);

它能以如下方式来使用:

 tape.play();
 tape.rewind(10);

基本上来说,你创造了一个拥有play,pause,stop和rewind方法的叫做tape的对象。这些所有的方法都是spy函数并可以按照我们在上面看到的方法来调用。

当我们的代码调用了一个外部接口时,这相当有用。


本系列文章译自Javascript testing with Jasmine,2013年3月由O’Reilly 出版社出版。

翻译文章需要花费大量时间和精力。如果您觉得本文对自己有帮助,请点击下面链接为我提供一些赞助。您的赞助是我继续前进的动力。

评论
发表评论
暂无评论
WRITTEN BY
张小俊128
Intern in Baidu mobile search department。认真工作,努力钻研,期待未来更多可能。
TA的新浪微博
PUBLISHED IN
基于Jasmine的Javascript测试

本系列文章译自Javascript testing with Jasmine,2013年3月由O’Reilly 出版社出版。

我的收藏