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

深入匹配器(matcher)


在Jasmine中已经内置了许多有用的匹配器。在本章的后面,你将学会如何构造你自己的匹配器。

相等:toEqual

Jasmine中最简单的匹配器可能就是toEqual了。它简单地检查两个东西是否相等(或者是否是相同的对象,在第五章中你会看到这点)。

下面的expect函数将能运行通过:

 expect(true).toEqual(true);
 expect([1, 2, 3]).toEqual([1, 2, 3]);
 expect({}).toEqual({});   

以下的几个例子将会运行失败:

 expect(5).toEqual(12);
 expect([1, 2, 3, 4]).toEqual([1, 2, 3]);
 expect(true).toEqual(100);

一定要始终对toEqual和toBe的区别保持清醒的认识。两者之间微妙的差别在下面将提及。

一致:toBe

第一眼看上去,toBe匹配器看起来很像toEqual匹配器,但它们并不完全相同。toBe检查两个东西是否是同一对象,而不仅仅检查它们是否相同。

这里有一个spec的例子将阐述toBe和toEqual的区别:

  var spot = { species: "Border Collie" };
  var cosmo = { species: "Border Collie" };
  expect(spot).toEqual(cosmo);  // 成功;等价
  expect(spot).toBe(cosmo);     // 失败;不是同一对象
  expect(spot).toBe(spot);      // 成功;是同一对象

正如我们所见,尽管spot和cosmo看起来非常相似而且相等,它们并不是同一对象。因为它们相等,但并不相同(注:javascript中对象的赋值都是引用赋值,只有指向同一引用的对象才能认为是同一对象)。

对于数组也同样适用:

  var arr = [1, 2, 3];
  expect(arr).toEqual([1, 2, 3]);  // 成功;等价
  expect(arr).toBe([1, 2, 3]);     // 失败;不是同一对象   

你可能注意到了toBe对于原始对象(数字,布尔值,字符串)来说时能正确工作的。这是因为Javascript中的 === 运算符将原始对象视为相同的实体。使用toBe事实上是在使用 === 运算符。

使用toEqual来检查原始数据类型是否等价,当然用toBe也能达到同样效果。使用toBe可能会打断你的测试进程,比如说你稍后决定在数组中改变了一个数字。

Javascript中关于两者之间的细微差别,可以观看视频JavaScript Primitive Types vs Reference Types。

对或错? toBeTruthy, toBeFalsy

测试一个东西是否是true,你可以使用toBeTruthy匹配器:

 expect(true).toBeTruthy();
 expect(12).toBeTruthy();
 expect({}).toBeTruthy();

同样的,测试一个东西是否是false,你可以使用toBeFalsy匹配器:

  expect(false).toBeFalsy();
  expect(null).toBeFalsy();
  expect("").toBeFalsy();

注意到Jasmine对truthy和falsy的判定方式与Javascript一致。这意味着true是truthy,“Hello world”也是,数字12也是,或者一个对象也是。思考一下哪些是falsy是很有帮助的,思考哪些是truthy也同样有用。

作为参考,我们在此列出Jasmine中被视作falsy的东西(在Javascript中也一样):

  • false
  • 0
  • “”
  • undefined (要注意到undefined的变量并不总是undefined!)
  • null
  • NaN

如果你之前没见过NaN,这是一个特殊的数字变量代表不是一个数字。他代表无意义的数字比如说0/0。它也可能作为返回值由一些函数返回(比如:parseInt(“hello”)返回NaN因为它无法解析出一个数字)。

如果你想要确定某个东西是true还是false或者其他。可以像这样使用toEqual匹配器:

  expect(myVariable).toEqual(true);
  expect(myOtherVariable).toEqual(false);

用not来否定其他匹配器

将Jasmine匹配器去翻让他们不是true常常是有用的。为了达到这个目的,只需简单地加一个.not的前缀即可:

   expect(foo).not.toEqual(bar);
   expect("Hello planet").not.toContain("world");

用toContain检查一个元素是否呈现

有时你想确认一个元素是否是一个数组的成员。为了达到此目的,你可以使用toContain匹配器:

 expect([1, 2, 3, 4]).toContain(3);
 expect(["Penguin", "Turtle", "Pig", "Duck"]).toContain("Duck");

注意到toContain并不检查数组是否包含完全相同的对象,因此下面你的例子将能运行成功:

  var dog = { name: "Fido" };
  expect([
    { name: "Spike" },
    { name: "Fido" },
    { name: "Spot" }
 ]).toContain(dog);

toContain匹配器在字符串中也能正确工作,正如我们在本书中看到的第一个例子:

    expect("Hello world").toContain("world");
    expect(favoriteCandy).not.toContain("Almond");

是否定义了?toBeDefined,toBeUndefined

正如有检查对与错的匹配器一样,Jasmine中同样也有检查定义和未定义的匹配器。

在我们开始之前,先让我们来简单回顾一下Javascript关于undefined的定义以及它是怎么和null进行比较的:当你声明了一个变量并且没有给它赋值,它的类型就变成了“undefined”(就像123的类型是“number”一样)。在其他语言中,它可能会是null或者nil。但在Javascript中则不是这样!关于这点有很多的疑惑,单着并不影响Jasmine的使用。

想要了解更多关于undefined的背景和它的工作原理,请查阅Understanding Javascript一书中的“undefined”部分。

下面是一些证明这些匹配器是怎样工作的例子:

 var somethingUndefined;
 expect("Hello!").toBeDefined();//成功
 expect(null).toBeDefined();//成功
 expect(somethingUndefined).toBeDefined();//失败 
 var somethingElseUndefined;
 expect(somethingElseUndefined).toBeUndefined();  //成功
 expect(12).toBeUndefined();                      //成功
 expect(null).toBeUndefined();                    // 失败

注意到你所检查的变量必须被定义是一件很有帮助的事。下面的代码抛出了一个引用错误并且运行失败:

   it("tests toBeUndefined", function() {
   expect(someUndefinedVariable).toBeUndefined();
   // 抛出一个引用错误因为变量someUndefinedVariable没有声明
 });

空值: toBeNull

tiBeNull匹配器的意思直接了当。如果你该没猜出来它的作用,它检查一个东西是否为空:

 expect(null).toBeNull();                //成功
 expect(false).toBeNull();               // 失败
 expect(somethingUndefined).toBeNull();  // 失败

很简单!不是吗?

是不是NaN? toBeNaN

和toBeNull一样,toBeNaN检查一个东西是否是NaN:

 expect(5).not.toBeNaN();              // 成功
 expect(0 / 0).toBeNaN();              // 成功
 expect(parseInt("hello")).toBeNaN();  // 成功

这和Javascript的内建isNaN函数不同。内建函数对于许多非数字类型都会返回true,例如非数字字符串,对象以及数组。Jasmine仅仅针对NaN返回true。

比较器:toBeGreaterThan,toBeLessThan

toBeGreaterThan 和 toBeLessThan比较器检查一个东西是否比另一个东西大或者小。下面所有的代码都能通过运行:

 expect(8).toBeGreaterThan(5);
 expect(5).toBeLessThan(12);
 expect("a").toBeLessThan("z"); // 注意到对于字符串依然能够运行

这简直太简单了!

接近程度:toBeCloseTo

toBeCloseTo允许你检查一个数字是否接近另一个数字,只需要给定一个小数精确程度作为第二个参数。

如果你想要确定一个变量是否在小数点后一位接近12.3,你的代码需要如下所示:

 expect(12.34).toBeCloseTo(12.3, 1); // 成功

如果你想要确定小数点后两位的精确程度,只需要把1改成2.这个spec将会运行失败,因为他们在小数点后第二位是不同的:

 expect(12.34).toBeCloseTo(12.3, 2); // 失败   

在这个例子中,任何大于2的数作为第二个参数都会导致运行失败,因此下面的代码也会运行失败:

 expect(12.34).toBeCloseTo(12.3, 3);  // 失败
 expect(12.34).toBeCloseTo(12.3, 4);  // 失败
 expect(12.34).toBeCloseTo(12.3, 5);  // 失败
 // 等等

将第二个参数设置为0可以有效地将数字近似为整数:

 expect(12.3456789).toBeCloseTo(12, 0);   // 成功
 expect(500).toBeCloseTo(500.087315, 0);  //成功
 expect(500.087315).toBeCloseTo(500, 0);  // 成功

我发现toBeCloseTo有一些限制和瑕疵。在下面的自定义匹配器一节中,我们将编写一个匹配器去提升TobeCloseTo的表现。

和正则表达式一起使用toMatch

toMatch检查一个东西是否匹配正则表达式。它能够以正则表达式或者字符串的形式被传递,后者也将被解析为一个正则表达式。以下的所有代码豆浆运行成功:

 expect("foo bar").toMatch(/bar/);
 expect("horse_ebooks.jpg").toMatch(/\w+.(jpg|gif|png|svg)/i);
 expect("jasmine@example.com").toMatch("\w+@\w+\.\w+");

想要了解更多Javascript中正则表达式的知识,可以查阅Mozilla Developer Network中一些非常有帮助的文章。(要注意到以上的正则表达式例子可能并不name完善--一个更好的email正则表达式会长得多!)

用toThrow检查一个函数是否抛出错误

toThrow让你表达出“嗨,我期待这个函数抛出一个错误”:

 var throwMeAnError = function() {
    throw new Error();
};
expect(throwMeAnError).toThrow();

你也可以使用匿名函数,那通常是很有用的。例如,让我们说有一个函数应该抛出一个异常连同一个错误输出,例如:

 calculate("BAD INPUT");  // 这应该抛出一个异常   

为了测试它,我们像这样使用Jasmine:

 expect(function() {
    calculate("BAD INPUT");
  }).toThrow();

不管你使用一个匿名还是命名函数,你始终应该传递一个函数因为Jasmine在运行specs时会调用它。

自定义匹配器

你也能创造自己的匹配器!你必须在每一个你想运行的spec之前添加匹配器。为了达到目的,我们将会使用beforeEach,更多关于此函数的细节将会在第五章中讨论。

我们假设你想添加一个叫做toBeLarge的匹配器,作用是检查一个数字是否大于100。在每个文件的顶部(或者在一个describe的顶部),你应该添加如下代码:

  beforeEach(function() {
      this.addMatchers({
          toBeLarge: function() {
              this.message = function() {
     return "Expected " + this.actual + " to be large";
               };
              return this.actual > 100;
          }
        }); 
   });   

这里需要一点关于Jasmine如何组织代码的知识。每一个匹配器从expect函数接收一个参数,对吧?expect(200)中的200就是它的参数。当我们在Jasmine中定义一个新的匹配器的时候这个参数就是this.actual;this.message这个函数的作用是当匹配器运行失败时返回解释信息。最后,我们返回一个布尔值表面this.actual是否是一个比100大的数。

现在,我们可以在specs中运行一下代码:
expect(5).toBeLarge(); // 失败 expect(200).toBeLarge(); // 成功 expect(12).not.toBeLarge(); // 成功

一个更复杂的匹配器也许想要用这种语法取代toBeClose:

  // 期望6在以5为中心2的范围之内(具体说是位于3到7之间)
  expect(6).toBeWithinOf(2, 5);

这个匹配器将接收两个参数,其余部分则和上面例子很类似:

  beforeEach(function() {
      this.addMatchers({
          toBeWithinOf: function(distance, base) {
              this.message = function() {
                  var lower = base - distance;
                  var upper = base + distance;
                  return "Expected " + this.actual + " to be between " +
                  lower + " and " + upper + " (inclusive)";
     };
              return Math.abs(this.actual - base) <= distance;
              }
       });
   });

在这个例子中,我们确保this.actual是到基准的距离。message函数计算了上下边界的值,匹配器的结果是一个简单地边界检查。


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

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

评论
发表评论
6年前

编辑下 支付宝链接,没有加https:// 前缀

编辑链接:http://www.html-js.com/edit-card

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

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

我的收藏