Write Descriptive Unit Tests

I am trying to write unit tests in Python and trying to find a descriptive way to do something. I have a JavaScript background and I use mocha , which helps me describe.

This is what I mean by β€œdescriptive”:

foo.js

 exports.foo = function (type, isLogged, iOk) { if (type === undefined) throw new Error('a cannot be undefined'); if (isLogged === undefined) throw new Error('b cannot be undefined'); if (isLogged) { if (type === 'WRITER') { return isOk ? "writer" : -1; } else { return "something else" } } } 

foo.spec.js

 describe('#foo()', function () { context('when type is undefined', function () { ... }) context('when isLogged is undefined', function () { ... }) context('when type is defined', function () { context('when isLogger is not defined', function () { ... }) context('when isLogged is defined', function () { context('when type is not WRITER', function () { ... }) context('when type is WRITER', function () { context('when isOk is true', function () { ... }) }) }) }) }) 

While I am writing unit tests in Python, I get something like this:

foo.spec.py

 class TestFoo: def test_when_type_is_undefined(self): ... def test_when_isLogged_is_undefined(self): ... # This test name is too long def test_when_type_is_defined_and_isLogged_is_undefined_and_type_is_writer_when_is_ok_is_true(self): ... 

What is the best way to structure these tests? What are the best practices for descriptive unit testing? Are there any good examples of good unit tests?

+2
javascript python unit-testing
Nov 22 '16 at 14:30
source share
3 answers

You can use pyspecs to have more BDD-like syntax in your code.

Example:

 from pyspecs import given, when, then, and_, the, this with given.two_operands: a = 2 b = 3 with when.supplied_to_the_add_function: total = a + b with then.the_total_should_be_mathmatically_correct: the(total).should.equal(5) with and_.the_total_should_be_greater_than_either_operand: the(total).should.be_greater_than(a) the(total).should.be_greater_than(b) with when.supplied_to_the_subtract_function: difference = b - a with then.the_difference_should_be_mathmatically_correct: the(difference).should.equal(1) 

Exit

 # run_pyspecs.py | β€’ given two operands | β€’ when supplied to the add function | β€’ then the total should be mathmatically correct | β€’ and the total should be greater than either operand | β€’ when supplied to the subtract function | β€’ then the difference should be mathmatically correct (ok) 6 passed (6 steps, 1 scenarios in 0.0002 seconds) 
+2
Nov 22 '16 at 2:36
source share

I see no problems with your unit test construct. Unit tests should be very descriptive, so it is obvious that the problem occurs after a failed test. As a developer with little information, the test_when_type_is_defined_and_isLogged_is_undefined_and_type_is_writer_when_is_ok_is_true test file tells me a lot about what went wrong and where to look.

You can make your tests even more descriptive by adding an error message to your assert so that when something fails, you know exactly why. For example: "The expected writer should be ok , but the writer was None."

To me, the name of the file where the test is located, the name of the test case and the confirmation message should give a clear path to which code failed and why.

0
Nov 22 '16 at 2:35
source share

Having meaningful test method names is important, of course, but when the test name becomes impractically long and unreadable, you can always provide complete test descriptions inside the documentation line method.

Here are some sample tests from the requests library :

 def test_cookielib_cookiejar_on_redirect(self, httpbin): """Tests resolve_redirect doesn't fail when merging cookies with non-RequestsCookieJar cookiejar. See GH #3579 """ cj = cookiejar_from_dict({'foo': 'bar'}, cookielib.CookieJar()) s = requests.Session() # ... def test_headers_on_session_with_None_are_not_sent(self, httpbin): """Do not send headers in Session.headers with None values.""" ses = requests.Session() ses.headers['Accept-Encoding'] = None # ... 

Note that you can see these docstrings on the console with increased verbosity . Demo video:

 $ cat test_module.py import unittest class BasicTestSuite(unittest.TestCase): def test_one(self): self.assertEqual(1, 1) def test_two(self): """Extended description""" self.assertEqual(2, 2) if __name__ == '__main__': unittest.main() $ python -m unittest -v test_module test_one (test_module.BasicTestSuite) ... ok test_two (test_module.BasicTestSuite) Extended description ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK 
0
Nov 22 '16 at 2:37
source share



All Articles