2020年9月13日 星期日

[ Python 文章收集 ] Arrange Act Assert pattern for Python developers

 Source From Here

What is Arrange Act Assert?
The “Arrange-Act-Assert” (also AAA and 3A) pattern of testing was observed and named by Bill Wake in 2001. I first came across it in Kent Beck’s book “Test Driven Development: By Example” and I spoke about it at PyConUK 2016.

The pattern focuses each test on a single action. The advantage of this focus is that it clearly separates the arrangement of the System Under Test (SUTand the assertions that are made on it after the action. On multiple projects I’ve worked on I’ve experienced organised and “clean” code in the main codebase, but disorganisation and inconsistency in the test suite. However when AAA is applied, I’ve found it helps by unifying and clarifying the structure of tests which helps make the test suite much more understandable and manageable.

Background
I’ll now go into detail on each of these parts using Pytest and a toy test example - a simple happy-path test for Python’s builtin list.reverse function. I’ve made the following assumptions:
* We all love PEP008, so we want tests to pass flake8 linting.
PEP020, The Zen of Python, is also something we work towards - I will use some of it’s “mantras” when I justify some of the suggestions in this guide.
* Simplicity trumps performance. We want a test suite that is easy to maintain and manage and can pay for that with some performance loss. I’ve assumed this is a reasonable trade off because the tests are run much less frequently than the SUT in production.

This post is only an introduction to the AAA pattern. Where certain topics will be covered in more detail in future posts in this series, I have marked them with a footnote.

Definition
The definition of the test function.

Example
  1. def test_reverse():  
Guidelines
* Name your function something descriptive because the function name will be shown when the test fails in Pytest output.
* Good test method names can make docstrings redundant in simple tests (thanks Adam!).


Docstring
An optional short single line statement about the behaviour under test.

Example
  1. """  
  2. list.reverse inverts the order of items in a list, in place  
  3. """  
Guidelines
Docstrings are not part of the AAA pattern. Consider if your test needs one or if you are best to omit it for simplicity. If you do include a docstring, then I recommend that you:
* Follow the existing Docstring style of your project so that the tests are consistent with the code base you are testing.
Keep the language positive - state clearly what the expected behaviour is. Positive docstrings read similar to:
Or…
  1. Given Z, then X does Y  
* Be cautious when using any uncertain language in the docstring and follow the mantra “Explicit is better than implicit” (PEP20)
Words like “should” and “if” introduce uncertainty. For example:
  1. X should do Y if Z  
In this case the reader could be left with questions. Is X doing it right at the moment? Is this a TODO note? Is this a test for an expected failure?

In a similar vein, avoid future case.
  1. X will do Y when Z  
Again, this reads like a TODO.

Arrange
The block of code that sets up the conditions for the test action.

Example
There’s not much work to do in this example to build a list, so the arrangement block is just one line.
  1. greek = ['alpha''beta''gamma''delta']  
Guidelines
* Use a single block of code with no empty lines.

* Do not use assert in the Arrange block. If you need to make an assertion about your arrangement, then this is a smell that your arrangement is too complicated and should be extracted to a fixture or setup function and tested in its own right.

* Only prepare non-deterministic results not available after action.

* The arrange section should not require comments. If you have a large arrangement in your tests which is complex enough to require detailed comments then consider:
- Extracting the comments into a multi-line docstring.
- Extracting the arrangement code into a fixture and testing that the fixture is establishing the expected conditions as previously mentioned.

Act
The line of code where the Action is taken on the SUT.

Example
  1. result = greek.reverse()  
Guidelines
* Start every Action line with result =.
This makes it easier to distinguish test actions and means you can avoid the hardest job in programming: naming. When every result is called result, then you do not need to waste brain power wondering if it should be item = or response = etc. An added benefit is that you can find test actions easily with a tool like grep.

* Even when there is no result from the action, capture it with result = and then assert result is None. In this way, the SUT’s behaviour is pinned.

* If you struggle to write a single line action, then consider extracting some of that code into your arrangement.

* The action can be wrapped in with ... raises for expected exceptions. In this case your action will be two lines surrounded by empty lines.

Assert
The block of code that performs the assertions on the state of the SUT after the action.

Example
  1. assert result is None  
  2. assert greek == ['delta''gamma''beta''alpha']  
Guidelines
* Use a single block of code with no empty lines.

* First test result, then side effects.

* Limit the actions that you make in this block. Ideally, no actions should happen, but that is not always possible.

* Use simple blocks of assertions. If you find that you are repeatedly writing the same code to extract information from the SUT and perform assertions on it, then consider extracting an assertion helper.

The final test
Here’s the example test in full:
  1. def test_reverse():  
  2.     """  
  3.     list.reverse inverts the order of items in a list, in place  
  4.     """  
  5.     greek = ['alpha''beta''gamma''delta']  
  6.   
  7.     result = greek.reverse()  
  8.   
  9.     assert result is None  
  10.     assert greek == ['delta''gamma''beta''alpha']  
flake8-aaa
Check out flake8-aaa - a Flake8 plugin that makes it easier to write tests that follow the Arrange Act Assert pattern outlined above.

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...