未加星标

Why I usepy.test

字体大小 | |
[开发(python) 所属分类 开发(python) | 发布者 店小二04 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏
A question came up in Slack at work recently: “What’s your favorite/recommended unit test framework [for python]?” I gave a brief recommendation at the time, but I thought it’d be worth writing up my opinionsproperly.

In Python, the standard library has a module for writing unit tests the aptly-named unittest but I tend to eschew this in favour of py.test . There are a few reasons I like py.test: my tests tends to be cleaner, have less boilerplate, and I get better test results. If you aren’t using py.test already, maybe I can persuade you tostart.

I’m assuming you’re already somewhat familiar with the idea of unit testing. If not, I’d recommend Ned Batchelder’s talk Getting Started Testing and Eevee’s post Testing, for people who hate testing .

So, why do I preferpy.test?

Lessboilerplate

When you write a test with unittest, you have to subclass unittest.TestCase and write your tests as methods on that class. Forexample:

import unittest class TestNumberFunctions(unittest.TestCase): def test_doubling(self): ... def test_tripling(self): ...

Whereas with py.test, you can just specify tests as top-levelfunctions:

def test_doubling(self): ... def test_tripling(self): ...

The py.test code is simpler and easier to write. I don’t have to worry about making a test class, subclassing from whatever unittest.TestCase is, adding self to all my test methods, and so on. That’s all unnecessary cruft that I no longer have to dealwith.

The unittest API design comes from JUnit , a testing framework for Java. In Java, all functions have to live as methods on a class, so this is a sensible design for a test framework. But Python doesn’t have that restriction, so we shouldn’t be bound byit.

Better asserts and erroroutput

When you use the unittest framework, you have to remember to use the special assert helpers . These produce more helpful diagnostics when a test fails. For example, consider these twotests:

class TestTheAnswer(unittest.TestCase): def test_with_regular_assert(self): assert the_answer() == 42 def test_with_assert_helpers(self): self.assertEqual(the_answer(), 42)

If the first test fails, all we get is a traceback pointing to the line that failed. That’s all the regular assert handler can tellus:

Traceback (most recent call last): File "answer.py", line 12, in test_with_regular_assert assert the_answer() == 42 AssertionError

We know the test failed, but we have no idea what value was actually returned by the_answer() . That could be incredibly helpful in debugging this failure, and it’s probably the first thing we’d look for if we got thistraceback.

If you use the assert helper, the error includes thisinformation:

Traceback (most recent call last): File "answer.py", line 9, in test_with_assert_helpers self.assertEqual(the_answer(), 42) AssertionError: 54 != 42

But in regular Python code, assert is a statement, not a function, and it’s the same for any sort of logical condition. These weird camel case functions are ugly.Ick.

In py.test, you just have to use regular assertstatements:

def test_the_answer(): assert the_answer() == 42

This is the sort of error output youget:

def test_the_answer(): > assert the_answer() == 42 E assert 54 == 42 E + where 54 = the_answer() hitchhikers.py:14: AssertionError

I much prefer this error output, and I didn’t even have to remember any specialmethods.

We get both sides of the equation, as with unittest. But we also see the error in the context of our code, which makes it easy to see exactly what failed, without having to open the file. Even better, it explains where special values came from, if not hard-coded in the assert for example, the function call that gaves us 54. This is very helpful fordebugging.

Even better, this can be used for more complex conditions that can’t be easily captured with the unittest assert helpers. Here’s the output from another testfailure:

def test_some_more(): > assert (the_purpose_of(life) is not None) or (the_answer() == 42) E assert (None is not None or 54 == 42) E + where None = the_purpose_of('life') E + and 54 = the_answer() arthur.py:16: AssertionError

I have no idea how py.test is handling asserts under the hood, but it’s reallyhelpful.

Parametrizedtests

Often, I want to run the same test with multiple test cases. With unittest, the best approach is to put them in a for loop:

class TestFizzBuzz(unittest.TestCase): def test_my_fizzbuzz(self): for n, result in [ (1, '1'), (3, 'fizz'), (5, 'buzz'), (6, 'fizz'), (15, 'fizzbuzz'), (16, '16'), ]: self.assertEqual(fizzbuzz(n), result)

The problem is, as soon as one of these cases fail, the test is failed and the other cases don’t run. You don’t get any information about possible problems with your other testcases.

With py.test, you can use the @pytest.mark.parametrize decorator to parametrize your tests. You provide a list of test cases, but each of

本文开发(python)相关术语:python基础教程 python多线程 web开发工程师 软件开发工程师 软件开发流程

主题: JavaSlackPython
分页:12
转载请注明
本文标题:Why I usepy.test
本站链接:http://www.codesec.net/view/480348.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(python) | 评论(0) | 阅读(33)