python预期库
pyexpect的Python项目详细描述
pyexpect:expect模式的最小但非常灵活的实现
expect模式的全部要点是允许生成可预测的良好错误消息的简明断言。
最好在示例中查看:
>>> expect(3).to.equal(4)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "expect.py", line 146, in __call__
AssertionError: Expect 3 to equal 4
行噪声被尽可能地减少,因此错误消息显示在尽可能靠近有问题的代码的地方。没有堆栈跟踪来挖掘、清除和一致的错误消息,告诉您出了什么问题。这就是断言的工作原理。
为什么要在self.assert*上使用expect()呢?
与python unittest模块使用的经典断言模式相比,这是最好的解释。除此之外,这些断言可以独立于任何unittest包使用。但让我们从一个例子开始:
self.assertEquals('foo', 'bar')
在这个断言中,不可能看到哪个参数是预期的,哪个是实际的值。虽然这个顺序在unittest包中的不同断言之间基本上是内部一致的(遗憾的是,大多数情况下是这样),但是在python中所有不同的单元测试包之间,尤其是在不同语言之间,它并不一致,这个模式已经被实现了。已启用。
更糟糕的是,一些框架会输出如下错误消息: (是的,我在看你!)
'bar' does not equal 'foo'
很容易花上几分钟的时间,直到你记起或者弄明白你的框架欺骗了你,并颠倒了参数的顺序,只是让你更难理解你正在阅读的代码。
如果你和我一样对此感到恼火,请允许我向你介绍expect模式。这样的断言:
expect('foo').to.equal('bar')
# or this
expect('foo').equals('bar')
# or even this
expect('foo') == 'bar'
使它绝对明白什么是预期的和什么是实际的价值。 不可能混淆。同时,错误消息被设计成清晰地映射回 到源代码:
Expect 'foo' to equal 'bar'.
因此,错误信息的映射是即时和完整的,每次都可以节省您几分钟的时间,提高您的专注度、工作效率,以及在使用单元测试时的最重要的乐趣。
另外,所有这些异常都没有耦合到任何testcase类,因此您可以轻松地在代码中的任何位置重用它们,以形式化代码对某些内部状态的期望。有时称为"按合同设计"或"快速失败"编程。哦,而且这些期望通常更短,所以您在测试中输入的断言更清晰、更切题,甚至更少。就像吃蛋糕一样!
有趣!那它能做什么呢?
很高兴你这么问!给您:
许多包含的匹配器:查看源代码以查看开始所需的所有断言。从
等于
到是
或是
并且提升
直到匹配
-我们已经覆盖了您。不仅如此,每个匹配器都有一些别名,因此您可以使用在断言中读得最好的变量,或者在跨语言边界使用多个单元测试框架时更习惯使用这种变量(python/js anyone?)一些示例:
expect(True).is_.true() expect(True).is_true() expect(True).equals(True) expect(True).is_.equal(True) expect(True) == True expect(raising_calable).raises() expect(raising_calable).to_raise()
如果缺少重要别名,欢迎请求拉取。
易用性:
expect()
可以任意链接您可以想到的任何内容(只要它是有效的python标识符),以便尽可能清晰地描述断言:expect(23).to.equal(23) expect(23).is_.equal(23) # or go all out - but just because it works doesn't mean it's sensible expect(23).to_be_chaned.with_something.that_makes_sense_in\ .your_context.before_it.calls_the.matcher.as_the_last.segment(23) # Here .segment(23) would be the matcher that is called
选择对您的特定测试有意义的内容,以便以后阅读测试时感觉很自然,并尽可能地传递代码的含义。
扩展的简单性:在我看过的所有其他python expect实现中,至少它们的某些方面要复杂得多。每个匹配器都是一个类或一些需要通过或多或少复杂的过程注册的东西,参数不仅仅是简单的方法参数,
不是
不支持作为本机框架概念…与PyExpect相比,如果您想注册一个新的匹配器,则可以很容易地定义一个方法,然后将其分配给任意多个实例方法名,断言您想在其中断言的内容,并明确定义将引发的错误消息:
def falseish(self): # See expect() source for availeable helpers self._assert(self._actual == False, "to be falseish") expect.is_falsish = expect.is_falseish = expect.falsish = expect.falseish = falseish
类预期(原始预期): 定义错误(自我): #有关可用的帮助程序,请参见expect()source self._assert(self._actual==false,"以假乱真")
完成了!还要注意匹配器如何清楚地传达重要的信息:它断言什么,以及它生成什么错误消息。不含绒毛™!
相比之下,您可以将匹配器添加到更为成熟的包中,如sure和确保-我认为pyexpect更简单-您可以只在
expect
或使用更多方法创建本地子类。native
not
支持:如果您定义了匹配器,则不必定义它的逆,也不必执行任何特殊的操作来获取它。这意味着,对于每一个匹配者,比如等于
,都会自动得到与之相反的结果,即不等于
。这个逆过程可以通过多种方式调用:您可以在匹配器前面加上
而不是这样的前缀
expect(foo).not_equals(bar) expect(some_function).not_to_raise()
您可以将
not
作为matcher前面路径的一部分,如下所示:
0>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
也就是说,您可以在标识符的开头、中间或结尾包含单词
而不是
-只需确保通过snakecase将其与标识符分开即可。这适用于每个匹配器的所有别名,因此不需要额外的工作。
有关更多示例,请查看匹配器的测试套件。
如果要添加自己的匹配项,如果将期望实现为多个连续检查,则有时反向操作不会自动工作。在这种情况下,反向匹配器可能会断言错误的内容,因为在反向情况下,检查的顺序没有意义。如果发生这种情况,请查看
expect.\u assert_if_positive()
,expect.\u assert_if_negative()
和expect.\u is_negative()
。不过,请注意,好的配对者只需要很少的时间。伟大的错误消息:pyexpect非常小心地确保遇到错误的每个方面都尽可能简洁和有用。所有错误消息的格式都相同,总是以预期的格式开头,然后由匹配者自定义,以尽可能多地打包信息。
1>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
如果您编写自己的断言方法来增强单元测试,则很容易获得长堆栈跟踪,因为实际断言会在一个调用的匹配器中发生一些堆栈帧。
考虑一下这样的断言(有点杜撰,好吧。但我相信您在您的项目中看到过这种情况:
2>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
它将提供如下输出:
3>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
即使在这种简单的情况下,实际误差也会从实际误差中去掉4行。
使用pyexpect进行如下测试:
4>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
提供此输出(标准
unittest.main()
):
5>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
在鼻测试中:
6>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
py.test
这更漂亮:
7>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
注意,实际的matcher
someting()
调用另一个方法\u assert
来执行实际的断言并合成错误消息,但是这些在堆栈跟踪中都不可见?对于您在matcher中调用的任何方法都是这样的,所以调用您的api或您需要触发断言并享受生成的错误消息的可读性的任何东西。另一个真正难以读取的错误消息的常见原因是消息太长。有过这样的经历吗?
8>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
祝你好运预期对象和实际对象的输出甚至是分开的,更不用说它们之间的区别了。
pyexpect在多行中格式化长错误消息,因此您总能看到投诉从何处开始,并在精神上更轻松地分散这两个对象:
9>>> expect(3).to.equal(4) Traceback (most recent call last): File "<input>", line 1, in <module> File "expect.py", line 146, in __call__ AssertionError: Expect 3 to equal 4
tl;dr:错误信息更容易阅读,而且错误和引起你分心的原因之间的差别也更小。应该是这样。
单元测试之外的用法:可以将此包用作一个独立的断言包,它提供了比仅使用
assert
和优化的错误消息来引导更具表现力的断言。只要在需要的地方断言就可以通过快速失败获得健壮的代码:
0self.assertEquals('foo', 'bar')
如果需要,可以将断言从抛出切换到返回
(bool,string)
元组,以便在api中重用它。
1self.assertEquals('foo', 'bar')
如果需要,可以重写生成的错误消息,而无需更改匹配器。有关详细信息,请参见
expect.with_message()
。测试覆盖率:当然,pyexpect有完整的测试覆盖率,确保它完全符合您的预期。
你认为这个文档中有更好的东西吗?发送拉取请求。:)< >