toBe(true) vs toBeTruthy() vs toBeTrue()

260
什么是 expect(something).toBe(true)expect(something).toBeTruthy()expect(something).toBeTrue() 之间的区别?
请注意,toBeTrue() 是一个自定义匹配器,它被引入到 jasmine-matchers 中,除此之外还有其他有用和方便的匹配器,如 toHaveMethod()toBeArrayOfStrings()
这个问题是通用的,但是作为一个实际的例子,我在 protractor 中测试一个元素是否显示。在这种情况下,我应该使用哪个匹配器?
expect(elm.isDisplayed()).toBe(true);
expect(elm.isDisplayed()).toBeTruthy();
expect(elm.isDisplayed()).toBeTrue();

4
我认为.toBe(true)等同于.toBeTrue()toBeTruthy()可以不仅在true时为真,也可以在123"dfgdfg"[1,2,3]等情况下为真,基本上if(x==true)是“真值”,而if(x===true)是“真的真值”。 - dandavis
3
这将取决于您要测试的值是什么。如果您不确定它的类型,则使用 toBeTruthy,它与 == true 相同,而我怀疑 .toBe(true)=== true 相同。请注意,调用函数来测试真实性有点过分了。建议,忘记在Javascript中存在 ==!=,永远不要再使用它们。Truthy 对于初学者来说并不需要,是一个陷阱。改用 ===!== - Blindman67
@Blindman67 谢谢你的建议,非常有道理。我们甚至使用 eslint 来报告是否使用了 ==!=,并建议将其更改为 ===!== - alecxe
5个回答

332

当我对像这里提出的问题产生疑惑时,我会去查阅来源。

toBe()

expect().toBe()的定义如下:

function toBe() {
  return {
    compare: function(actual, expected) {
      return {
        pass: actual === expected
      };
    }
  };
}

它使用 === 进行测试,这意味着当使用 expect(foo).toBe(true) 时,仅当 foo 实际上具有值true时,测试才通过。Truthy值不会使测试通过。

toBeTruthy()

expect().toBeTruthy()被定义为:

function toBeTruthy() {
  return {
    compare: function(actual) {
      return {
        pass: !!actual
      };
    }
  };
}

类型强制转换

当将某个值转换为布尔类型并得到值 true 时,该值被称为真值。操作符 !! 通过将传递给 expect 的值强制转换为布尔类型来测试其是否为真值。需要注意的是,与当前被接受的回答所暗示的内容相反,== true 不是正确测试真值的方法。你可能会遇到一些奇怪的情况。

> "hello" == true
false
> "" == true
false
> [] == true
false
> [1, 2, 3] == true
false

而使用!!则会得到:

> !!"hello"
true
> !!""
false
> !![1, 2, 3]
true
> !![] 
true

(是的,无论数组是否为空,都是真实值。)

toBeTrue()

expect().toBeTrue()Jasmine-Matchers 的一部分(注册在 npm 上作为 jasmine-expect,后来另一个项目先注册了jasmine-matchers)。

expect().toBeTrue() 的定义如下:

function toBeTrue(actual) {
  return actual === true ||
    is(actual, 'Boolean') &&
    actual.valueOf();
}
expect().toBeTrue()expect().toBe(true)的区别在于expect().toBeTrue()测试它是否处理一个Boolean对象。expect(new Boolean(true)).toBe(true)会失败,而expect(new Boolean(true)).toBeTrue()会通过。这是因为有趣的事情是:
> new Boolean(true) === true
false
> new Boolean(true) === false
false

至少它是真实的:

> !!new Boolean(true)
true

哪个最适合与elem.isDisplayed()一起使用?

最终,Protractor将此请求交给Selenium。 文档 指出,.isDisplayed() 返回的值是一个解析为boolean的承诺。我会以面值接受它并使用.toBeTrue().toBe(true)。如果我发现实现返回真值/假值的情况,我会提交错误报告。


41

免责声明:这只是一个猜测

我知道大家都喜欢易读的列表:

  • toBe(<value>) - 返回值等同于<value>
  • toBeTrue() - 检查返回值是否为true
  • toBeTruthy() - 检查值在转换为布尔值时是否为真值

    真值指除了0''(空字符串),falsenullNaNundefined[](空数组)之外的所有值。

    * 注意,当运行 !![] 时,它会返回true,但当你运行[]==false时,它也会返回true。这取决于如何实现。 换句话说:(!![]) === ([] == false)


在您的示例中,toBe(true)toBeTrue()将产生相同的结果。


一个空数组是假值。 - micah
@MicahWilliamson 谢谢!已修正答案。 - Ismael Miguel
4
在JavaScript中,空数组是 100% 真值(truthy),因此运行 alert(!![]) 会弹出一个对话框,显示 true。 - dandavis
@给踩的人 - 请解释一下为什么要踩我,这样我才能相应地修正我的回答。 - Ismael Miguel
2
你的评论询问为什么被踩了。我想你可能仍然会欣赏一个解释(不要发表“瞎猜”)。无论如何,在这一点上,请记住,答案对于未来的读者来说比原始提问者更重要,因此将答案从“瞎猜”改进为明确的答案永远不会太晚。 - kjhughes
显示剩余7条评论

28

在JavaScript中,有“真”和“真值”之分。当某个值为“真”时,它显然是真或假的。当某个值为“真值”时,它可能是布尔值,也可能不是,但是其“强制类型转换”后的值是布尔值。

例如:

true == true; // (true) true
1 == true; // (true) truthy
"hello" == true;  // (true) truthy
[1, 2, 3] == true; // (true) truthy
[] == false; // (true) truthy
false == false; // (true) true
0 == false; // (true) truthy
"" == false; // (true) truthy
undefined == false; // (true) truthy
null == false; // (true) truthy

如果您想检查一个字符串是否设置或者数组中是否有任何值,这样可以使事情更简单。

var users = [];

if(users) {
  // this array is populated. do something with the array
}

var name = "";

if(!name) {
  // you forgot to enter your name!
}

正如所述,expect(something).toBe(true)expect(something).toBeTrue()是相同的。但是expect(something).toBeTruthy()与这两者都不同。


2
[] == false; 这个语句是错误的,因为对象总是真实的。 - dandavis
2
不,它并没有用处,事实上恰恰相反:这是一个新手陷阱...考虑[""]==false[0]== false; 不是空的,也不是假值,只是具有欺骗性... - dandavis
有趣。可能应该坚持使用 if(users.length) - micah
3
在你的示例中使用x == true是一种误导性的方式,正如上面的评论所显示的那样,这是一种不正确的展示JavaScript真值概念的方法。在JavaScript中真值的真正测试是一个值在if语句中的表现或作为布尔表达式中的操作数。我们知道1被视为真值,因为if (1)会导致下一个语句被评估。同样,[]也是真值,原因相同:即使[] == true的结果为falseif([])仍将导致下一个语句被评估,所以我们知道[]是真值。 - Jordan Running
你应该使用 === 进行比较,而不是 ==,因此我不相信你的例子是可靠的。 - Sanjay
显示剩余3条评论

8
阅读下面的示例时,只需记住这个区别。
true === true // true
"string" === true // false
1 === true // false
{} === true // false

但是

Boolean("string") === true // true
Boolean(1) === true // true
Boolean({}) === true // true

1. expect(statement).toBe(true)

expect()函数接收一个statement参数,断言将会通过当该statement求值结果为true

expect(true).toBe(true) // pass
expect("123" === "123").toBe(true) // pass

在所有其他情况下,它都会失败。

expect("string").toBe(true) // fail
expect(1).toBe(true); // fail
expect({}).toBe(true) // fail

尽管在执行 Boolean() 时,这些语句都会被评估为 true

但你可以将它视为“严格”比较

2. expect(statement).toBeTrue()

这个函数与 .toBe(true) 执行完全相同类型的比较,但是在 Jasmine 版本 3.5.0 中最近引入于2019年9月20日

3. expect(statement).toBeTruthy()

然而toBeTruthy首先将语句的输出值转换为布尔值,然后再进行比较。

expect(false).toBeTruthy() // fail
expect(null).toBeTruthy() // fail
expect(undefined).toBeTruthy() // fail
expect(NaN).toBeTruthy() // fail
expect("").toBeTruthy() // fail
expect(0).toBeTruthy() // fail

在所有其他情况下,它都会通过,例如:

expect("string").toBeTruthy() // pass
expect(1).toBeTruthy() // pass
expect({}).toBeTruthy() // pass

3

有许多好的答案,我只想补充一个情景,在这种情况下使用这些期望可能会有所帮助。使用element.all(xxx),如果我需要检查所有元素是否在单个运行中显示,我可以执行 -

expect(element.all(xxx).isDisplayed()).toBeTruthy(); //Expectation passes
expect(element.all(xxx).isDisplayed()).toBe(true); //Expectation fails
expect(element.all(xxx).isDisplayed()).toBeTrue(); //Expectation fails

原因是.all()返回一个值数组,所以当.all()出现时,可以执行各种期望(getTextisPresent等),并使用toBeTruthy()进行验证。希望这可以帮到您。


不错!我记得将布尔数组“reduce()”为单个值,然后应用“toBe(true)”检查。这样更简单,谢谢。 - alecxe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接