Pytest Example and Cheat Sheet 小抄

Pytest 常用的情境小抄

pytest.fixture

Scenario:

  • Set up OS environment for every test cases.
  • Set up Git tag to running environment.
  • setup / teardown

pytest.fixture(scope="XXX")

  • Available scope:
    • function (default)
    • class
    • module
    • session
@pytest.fixture(scope="function", autospec=True)
def set_env():
	os.environ["ENV"] = "dev"
	os.environ["OWNER"] = "frs"
	os.environ["SYS"] = "trx"
	os.environ["COMP"] = "xshield"

ref. pytest中的fixture

pytest.mark.parametrize

Scenario

  • Multiple sets of arguments and expected results.
  • like PHPUnit - @dataProvider annotation
@pytest.mark.parametrize("test_input, expected", [
    ([1, 1], 2),
    ([2, 2], 4),
    ([0, 1], 1),
])
def test_add(test_input, expected):
    assert expected == add(test_input[0], test_input[1])

ref. Parametrizing fixtures and test functions - pytest documentation

pytest.raises(Exception)

Scenario

  • Catch the exception from function error running
  • Verify the exception message
def test_zero_division():
    with pytest.raises(ZeroDivisionError): # Catch ZeroDivisionError Exception
        1 / 0   # Expected error happen here!!
def test_recursion_depth():
    with pytest.raises(BaseException) as excinfo:
				some_func_raise_exception()   # Expected error happen here!!

    assert "Exception Message Which You Want To Assert" in str(excinfo.value)

The writing and reporting of assertions in tests - pytest documentation

capsys.readouterr()

Scenario

  • Capture of the stdout/stderr output
def test_output(capsys):  # or use "capfd" for fd-level
    print("hello")
    sys.stderr.write("world\n")

    captured = capsys.readouterr()
    assert captured.out == "hello\n"      # standard output
    assert captured.err == "world\n"      # standard error

side_effect

Scenario

  • Override calls
  • Patch method which can return different value depends on input argument.
  • Raise an exception when calling the Mock
# custom function
def get_foo_bar(key):
	return "foo" if key == "foo" else "bar"

def test_with_diff_ssm_value(mocker):
      event = {"foo": "bar", "ssm_input": "foo"}

			# assign the side_effect to return_value
      m_ssm = mocker.patch("SSMParameterStore", autospec=True)
      m_ssm.return_value.get_param.side_effect = get_foo_bar

			# mock method will return value by custom function
      actual = lambda_handler(event, None)

      expect = {"foo": "bar", "ssm_value": "foo"}

      assert actual == expect
def test_unexpected_error(mocker):
      m_obj = mocker.patch("aws.emr.get_step_status", side_effect=TypeError)

      event = {"pattern_label": {"emr": {"id": "j-3FVHTO6QGW09Q"}}}

      with pytest.raises(SomeException):
          lambda_handler(event, None)

      assert m_obj.call_count == 1

Mocker Object Assertions

Mocker Object Assertions you may need

Mocker Property

  • call_once
  • call_count
  • call_args
assert mocker_obj.call_once
assert mocker_obj.call_count == 3
assert mocker_obj.call_args == ["dev-xshield", 'TlshLabel', "day_fn_count"]

Mocker method

  • assert_called_once()
  • assert_not_called()
  • assert_called_once_with(“args”, “input”)
mocker_obj.assert_called_once()
mocker_obj.assert_not_called()
mocker_obj.assert_called_once_with("args", "input")