From f532c80fbf129c17db9c7e2184de7d88e08fab1d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 17 Mar 2020 16:10:41 +0000 Subject: [PATCH 1/2] Improve test quality and coverage --- library/tests/conftest.py | 38 +++++++++++++++++++++++++ library/tests/test_dimensions.py | 18 ++++-------- library/tests/test_features.py | 26 +++++++++++++++++ library/tests/test_setup.py | 48 +++++++++++++++++++++++++++----- library/tests/tools.py | 23 +++++++++++++++ library/tox.ini | 2 +- 6 files changed, 134 insertions(+), 21 deletions(-) create mode 100644 library/tests/conftest.py create mode 100644 library/tests/test_features.py create mode 100644 library/tests/tools.py diff --git a/library/tests/conftest.py b/library/tests/conftest.py new file mode 100644 index 0000000..7582e08 --- /dev/null +++ b/library/tests/conftest.py @@ -0,0 +1,38 @@ +"""Test configuration. +These allow the mocking of various Python modules +that might otherwise have runtime side-effects. +""" +import sys +import mock +import pytest + + +@pytest.fixture(scope='function', autouse=False) +def GPIO(): + """Mock RPi.GPIO module.""" + GPIO = mock.MagicMock() + # Fudge for Python < 37 (possibly earlier) + sys.modules['RPi'] = mock.Mock() + sys.modules['RPi'].GPIO = GPIO + sys.modules['RPi.GPIO'] = GPIO + yield GPIO + del sys.modules['RPi'] + del sys.modules['RPi.GPIO'] + + +@pytest.fixture(scope='function', autouse=False) +def spidev(): + """Mock spidev module.""" + spidev = mock.MagicMock() + sys.modules['spidev'] = spidev + yield spidev + del sys.modules['spidev'] + + +@pytest.fixture(scope='function', autouse=False) +def numpy(): + """Mock numpy module.""" + numpy = mock.MagicMock() + sys.modules['numpy'] = numpy + yield numpy + del sys.modules['numpy'] diff --git a/library/tests/test_dimensions.py b/library/tests/test_dimensions.py index 8de9af4..4eb4b61 100644 --- a/library/tests/test_dimensions.py +++ b/library/tests/test_dimensions.py @@ -1,24 +1,16 @@ -import sys -import mock +from tools import force_reimport -def _mock(): - sys.modules['numpy'] = mock.Mock() - sys.modules['spidev'] = mock.Mock() - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - - -def test_128_64_0(): - _mock() +def test_128_64_0(GPIO, spidev, numpy): + force_reimport('ST7735') import ST7735 display = ST7735.ST7735(port=0, cs=0, dc=24, width=128, height=64, rotation=0) assert display.width == 128 assert display.height == 64 -def test_128_64_90(): - _mock() +def test_128_64_90(GPIO, spidev, numpy): + force_reimport('ST7735') import ST7735 display = ST7735.ST7735(port=0, cs=0, dc=24, width=128, height=64, rotation=90) assert display.width == 64 diff --git a/library/tests/test_features.py b/library/tests/test_features.py new file mode 100644 index 0000000..a7e95e9 --- /dev/null +++ b/library/tests/test_features.py @@ -0,0 +1,26 @@ +import sys +import mock +from tools import force_reimport + + +def test_display(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + display = ST7735.ST7735(port=0, cs=0, dc=24) + numpy.dstack().flatten().tolist.return_value = [0xff, 0x00, 0xff, 0x00] + display.display(mock.MagicMock()) + + spidev.SpiDev().xfer3.assert_called_with([0xff, 0x00, 0xff, 0x00]) + + +def test_color565(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + assert ST7735.color565(255, 255, 255) == 0xFFFF + + +def test_image_to_data(GPIO, spidev, numpy): + force_reimport('ST7735') + numpy.dstack().flatten().tolist.return_value = [] + import ST7735 + assert ST7735.image_to_data(mock.MagicMock()) == [] diff --git a/library/tests/test_setup.py b/library/tests/test_setup.py index ac8bb04..b63a85a 100644 --- a/library/tests/test_setup.py +++ b/library/tests/test_setup.py @@ -1,13 +1,47 @@ -import sys import mock +from tools import force_reimport -def test_setup(): - sys.modules['numpy'] = mock.Mock() - sys.modules['spidev'] = mock.Mock() - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - +def test_setup(GPIO, spidev, numpy): + force_reimport('ST7735') import ST7735 display = ST7735.ST7735(port=0, cs=0, dc=24) del display + + GPIO.output.assert_has_calls([ + mock.call(24, True), + mock.call(24, False) + ], any_order=True) + + +def test_setup_no_invert(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + display = ST7735.ST7735(port=0, cs=0, dc=24, invert=False) + del display + + +def test_setup_with_backlight(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + display = ST7735.ST7735(port=0, cs=0, dc=24, backlight=4) + GPIO.setup.assert_called_with(4, GPIO.OUT) + + display.set_backlight(GPIO.HIGH) + + GPIO.output.assert_has_calls([ + mock.call(4, GPIO.LOW), + mock.call(4, GPIO.HIGH), + # Dozens of falls with True/False here + # due to _init() being called and the display + # setup setting the command/data pin + mock.call(4, GPIO.HIGH) + ], any_order=True) + + +def test_setup_with_reset(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + display = ST7735.ST7735(port=0, cs=0, dc=24, rst=4) + GPIO.setup.assert_called_with(4, GPIO.OUT) + del display \ No newline at end of file diff --git a/library/tests/tools.py b/library/tests/tools.py new file mode 100644 index 0000000..2050856 --- /dev/null +++ b/library/tests/tools.py @@ -0,0 +1,23 @@ +import sys + + +def force_reimport(module): + """Force the module under test to be re-imported. + + Because pytest runs all tests within the same scope (this makes me cry) + we have to do some manual housekeeping to avoid tests polluting each other. + + Since conftest.py already does some sys.modules mangling I see no reason not to + do the same thing here. + """ + if "." in module: + steps = module.split(".") + else: + steps = [module] + + for i in range(len(steps)): + module = ".".join(steps[0:i + 1]) + try: + del sys.modules[module] + except KeyError: + pass diff --git a/library/tox.ini b/library/tox.ini index e53234d..b23507c 100644 --- a/library/tox.ini +++ b/library/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{27,35},qa +envlist = py{27,35,37},qa skip_missing_interpreters = True [testenv] From 6f94d98040b442d4ac131512037822c3ca61b91e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 20 Mar 2020 12:00:31 +0000 Subject: [PATCH 2/2] Make stickler happy --- library/tests/test_features.py | 1 - library/tests/test_setup.py | 2 +- library/tests/tools.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/library/tests/test_features.py b/library/tests/test_features.py index a7e95e9..f8fb7d4 100644 --- a/library/tests/test_features.py +++ b/library/tests/test_features.py @@ -1,4 +1,3 @@ -import sys import mock from tools import force_reimport diff --git a/library/tests/test_setup.py b/library/tests/test_setup.py index b63a85a..8b89f47 100644 --- a/library/tests/test_setup.py +++ b/library/tests/test_setup.py @@ -44,4 +44,4 @@ def test_setup_with_reset(GPIO, spidev, numpy): import ST7735 display = ST7735.ST7735(port=0, cs=0, dc=24, rst=4) GPIO.setup.assert_called_with(4, GPIO.OUT) - del display \ No newline at end of file + del display diff --git a/library/tests/tools.py b/library/tests/tools.py index 2050856..ddf752d 100644 --- a/library/tests/tools.py +++ b/library/tests/tools.py @@ -14,7 +14,7 @@ def force_reimport(module): steps = module.split(".") else: steps = [module] - + for i in range(len(steps)): module = ".".join(steps[0:i + 1]) try: