From 4028e78b6cdbbd64f89bc17ac9b7d1030e229788 Mon Sep 17 00:00:00 2001 From: mxochicale Date: Sat, 23 Dec 2023 08:46:28 +0000 Subject: [PATCH] created pytest path, tidies scripts and READMEs and updates eVE.yaml #54 --- pyVEs/bareVE.yml | 2 +- pyVEs/eVE.yml | 37 +++++++---- pytest/README.md | 22 +++++++ pytest/fig-testing-workflows.png | Bin 0 -> 42618 bytes .../tests/test_mocker_sums.py | 2 +- pytest/tests/test_my_class.py | 33 ++++++++++ pytest/tests/week05-testing/README.md | 49 +++++++++++++++ .../tests}/week05-testing/fixture.yaml | 0 .../tests}/week05-testing/test_times.py | 0 .../tests}/week05-testing/times.py | 17 ++--- pytest/tests/yaml_files/conftest.py | 59 ++++++++++++++++++ pytest/tests/yaml_files/test_simple.yaml | 8 +++ python/pytest/README.md | 6 -- python/pytest/unittest_mock/README.md | 7 --- .../pytest/unittest_mock/examples/README.md | 2 - .../examples/patch_examples/my_class.py | 34 ---------- .../examples/week05-testing/README.md | 14 ----- 17 files changed, 208 insertions(+), 84 deletions(-) create mode 100644 pytest/README.md create mode 100644 pytest/fig-testing-workflows.png rename python/pytest/unittest_mock/examples/patch_examples/example00.py => pytest/tests/test_mocker_sums.py (91%) create mode 100644 pytest/tests/test_my_class.py create mode 100644 pytest/tests/week05-testing/README.md rename {python/pytest/unittest_mock/examples => pytest/tests}/week05-testing/fixture.yaml (100%) rename {python/pytest/unittest_mock/examples => pytest/tests}/week05-testing/test_times.py (100%) rename {python/pytest/unittest_mock/examples => pytest/tests}/week05-testing/times.py (82%) create mode 100644 pytest/tests/yaml_files/conftest.py create mode 100644 pytest/tests/yaml_files/test_simple.yaml delete mode 100644 python/pytest/README.md delete mode 100644 python/pytest/unittest_mock/README.md delete mode 100644 python/pytest/unittest_mock/examples/README.md delete mode 100644 python/pytest/unittest_mock/examples/patch_examples/my_class.py delete mode 100644 python/pytest/unittest_mock/examples/week05-testing/README.md diff --git a/pyVEs/bareVE.yml b/pyVEs/bareVE.yml index 4e8473c..1e3f3e4 100644 --- a/pyVEs/bareVE.yml +++ b/pyVEs/bareVE.yml @@ -4,6 +4,7 @@ ## INSTALL MAMBA EV: mamba env create -f *VE.yml ## ACTIVATE MAMBA ENV: mamba activate *VE ## REMOVE MAMBA ENV: mamba remove -n *VE --all +## mamba env update --prune -n bareVE -f bareVE.yaml name: bareVE channels: @@ -24,7 +25,6 @@ dependencies: - vtk>=9.3.0 ### VERSIONS of opencv-contrib-python-headless https://pypi.org/project/opencv-contrib-python-headless/#history - opencv-contrib-python-headless>=4.7.0.68 - # - h5py #- pyvista #https://pypi.org/project/pyvista/#history #https://github.com/pyvista/pyvista/tags #- qimage2ndarray diff --git a/pyVEs/eVE.yml b/pyVEs/eVE.yml index b557aff..b469413 100644 --- a/pyVEs/eVE.yml +++ b/pyVEs/eVE.yml @@ -5,24 +5,39 @@ ## UPDATE MAMBA ENV: mamba env update -f *VE.yml --prune ## ACTIVATE MAMBA ENV: mamba activate *VE ## REMOVE MAMBA ENV: mamba remove -n *VE --all +## mamba env update --prune -n eVE -f eVE.yaml name: eVE # experimental and light Virtual Environmnet channels: - - conda-forge #pyside6 + - conda-forge dependencies: - - python=3.11 #- python=3.8 + #- python=3.9 + #- python=3.10 + - python=3.11 - pip - pip: - - pyside6 #https://pypi.org/project/PySide6/#history - - matplotlib - - opencv-python - - scikit-learn - - notebook - - pandas - - seaborn - #- opencv-contrib-python-headless #https://pypi.org/project/opencv-contrib-python-headless/#history - - araviq6 + - pytest + - pytest-mock + - pyyaml + - requests + ### VERSIONS of pyside6: https://pypi.org/project/PySide6/#history + #- pyside6>=6.4.2 + ### VERSIONS of VTK https://gitlab.kitware.com/vtk/vtk/-/tags + #- vtk>=9.0.3 + #- vtk>=9.2.0 + #- vtk>=9.2.5 + #- vtk>=9.3.0 + ### VERSIONS of opencv-contrib-python-headless https://pypi.org/project/opencv-contrib-python-headless/#history + #- opencv-contrib-python-headless>=4.7.0.68 + #- opencv-contrib-python-headless + #- opencv-python + #- matplotlib + #- scikit-learn + #- notebook + #- pandas + #- seaborn + #- araviq6 #- civiq6 #- cv2PySide6 #- qimage2ndarray diff --git a/pytest/README.md b/pytest/README.md new file mode 100644 index 0000000..c4661dd --- /dev/null +++ b/pytest/README.md @@ -0,0 +1,22 @@ +# Python Testing with Pytest + +## Unit testing +The typical testing workflow is showing below ([py-pkgs-testing](https://py-pkgs.org/05-testing.html)). + + +``` +mamba activate eVE +pytest -sv tests/yaml_files/test_simple.yaml +``` +* Options +``` +#-s option. All print statements -v it's a short --verbose +-m? +-k? +``` + +## References +* https://github.com/jashburn8020/python-testing-with-pytest +* https://docs.python.org/dev/library/unittest.mock.html +* https://pypi.org/project/pytest-mock/ + diff --git a/pytest/fig-testing-workflows.png b/pytest/fig-testing-workflows.png new file mode 100644 index 0000000000000000000000000000000000000000..216f14573d50af466ebf4377e3c215e470dd386a GIT binary patch literal 42618 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 iss_over_London = iss_passes(51.5074, -0.1278) + +test_times.py:61: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +times.py:35: in iss_passes + return [(datetime.datetime.fromtimestamp(x['risetime']).strftime("%Y-%m-%d %H:%M:%S"), +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +.0 = + +> return [(datetime.datetime.fromtimestamp(x['risetime']).strftime("%Y-%m-%d %H:%M:%S"), + (datetime.datetime.fromtimestamp(x['risetime'] + x['duration'])).strftime("%Y-%m-%d %H:%M:%S")) + for x in response] +E TypeError: string indices must be integers, not 'str' + +times.py:35: TypeError +--------------------------------------------------------------- Captured stdout call --------------------------------------------------------------- +200 +{'message': 'success', 'request': {'altitude': 10.0, 'datetime': 1703321064.740006, 'latitude': 51.5074, 'longitude': -0.1278, 'passes': 5}, 'response': [{'duration': 446, 'risetime': 1703409497.740006}, {'duration': 628, 'risetime': 1703415159.740006}, {'duration': 656, 'risetime': 1703420935.740006}, {'duration': 655, 'risetime': 1703426740.740006}, {'duration': 632, 'risetime': 1703432544.740006}]} +============================================================= short test summary info ============================================================== +FAILED test_times.py::test_iss_passes - TypeError: string indices must be integers, not 'str' +=========================================================== 1 failed, 6 passed in 0.14s ================================= + +``` + +## References +* https://github.com/UCL-COMP0233-22-23/times-tests +* https://github.com/UCL-MPHY0021-21-22/RSE-Classwork/issues/16 +* https://gist.github.com/alessandrofelder/8fb8e34f29970cd8e05c4c18d30f98f5 +` diff --git a/python/pytest/unittest_mock/examples/week05-testing/fixture.yaml b/pytest/tests/week05-testing/fixture.yaml similarity index 100% rename from python/pytest/unittest_mock/examples/week05-testing/fixture.yaml rename to pytest/tests/week05-testing/fixture.yaml diff --git a/python/pytest/unittest_mock/examples/week05-testing/test_times.py b/pytest/tests/week05-testing/test_times.py similarity index 100% rename from python/pytest/unittest_mock/examples/week05-testing/test_times.py rename to pytest/tests/week05-testing/test_times.py diff --git a/python/pytest/unittest_mock/examples/week05-testing/times.py b/pytest/tests/week05-testing/times.py similarity index 82% rename from python/pytest/unittest_mock/examples/week05-testing/times.py rename to pytest/tests/week05-testing/times.py index 7c89131..a2b0409 100644 --- a/python/pytest/unittest_mock/examples/week05-testing/times.py +++ b/pytest/tests/week05-testing/times.py @@ -1,11 +1,6 @@ import datetime import requests -#References -# https://github.com/UCL-COMP0233-22-23/times-tests -# https://github.com/UCL-MPHY0021-21-22/RSE-Classwork/issues/16 -# https://gist.github.com/alessandrofelder/8fb8e34f29970cd8e05c4c18d30f98f5 - def time_range(start_time, end_time, number_of_intervals=1, gap_between_intervals_s=0): start_time_s = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S") end_time_s = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S") @@ -22,15 +17,21 @@ def iss_passes(lat, lon, n=5): Returns a time_range-like output for intervals of time when the International Space Station passes at a given location and for a number of days from today. """ - iss_request = requests.get("http://api.open-notify.org/iss-pass.json", + #http://open-notify.org/Open-Notify-API/ + #iss_request = requests.get("http://api.open-notify.org/iss-pass.json") #404 + #iss_request = requests.get("http://api.open-notify.org/iss-pass.json", + iss_request = requests.get("http://api.open-notify.org/iss-now.json", params={ "lat": lat, "lon": lon, - "n": n}) + "n": n}) + print(iss_request.status_code) if iss_request.status_code != 200: # if the request failed for some reason return [] - response = iss_request.json()['response'] + #response = iss_request.json()['response'] + response = iss_request.json() + print(response) return [(datetime.datetime.fromtimestamp(x['risetime']).strftime("%Y-%m-%d %H:%M:%S"), (datetime.datetime.fromtimestamp(x['risetime'] + x['duration'])).strftime("%Y-%m-%d %H:%M:%S")) for x in response] diff --git a/pytest/tests/yaml_files/conftest.py b/pytest/tests/yaml_files/conftest.py new file mode 100644 index 0000000..9a0fd8a --- /dev/null +++ b/pytest/tests/yaml_files/conftest.py @@ -0,0 +1,59 @@ +# content of conftest.py +import pytest + +def pytest_collect_file(parent, file_path): + print(f"\n") + print(f'PRINT pytest_collect_file > parent: {parent}') + print(f'PRINT pytest_collect_fiel > file_path: {file_path}') + if file_path.suffix == ".yaml" and file_path.name.startswith("test"): + return YamlFile.from_parent(parent, path=file_path) + + +class YamlFile(pytest.File): + def collect(self): + # We need a yaml parser, e.g. PyYAML. + import yaml + + print(f'PRINT YamlFile > pytest.File: {pytest.File}') + print(f'PRINT YamlFile > self: {self}') + + raw = yaml.safe_load(self.path.open()) + print(f'PRINT YamlFile > raw: {raw}') + for name, spec in sorted(raw.items()): + print(f' PRINT YamlFile > name: {name}') + print(f' PRINT YamlFile > spec: {spec}') + yield YamlItem.from_parent(self, name=name, spec=spec) + + +class YamlItem(pytest.Item): + def __init__(self, *, spec, **kwargs): + super().__init__(**kwargs) + self.spec = spec + #print(f'PRINT2 YamlItem > self.spec: {self.spec}') + + def runtest(self): + for name, value in sorted(self.spec.items()): + # Some custom test execution (dumb example follows). + print(f"\n") + print(f'PRINT2 YamlItem::runtest() > name: {name}') + print(f'PRINT2 YamlItem::runtest() > value: {value}') + if name != value: + raise YamlException(self, name, value) + + def repr_failure(self, excinfo): + """Called when self.runtest() raises an exception.""" + if isinstance(excinfo.value, YamlException): + return "\n".join( + [ + "usecase execution failed", + " spec failed: {1!r}: {2!r}".format(*excinfo.value.args), + " no further details known at this point.", + ] + ) + + def reportinfo(self): + return self.path, 0, f"usecase: {self.name}" + + +class YamlException(Exception): + """Custom exception for error reporting.""" diff --git a/pytest/tests/yaml_files/test_simple.yaml b/pytest/tests/yaml_files/test_simple.yaml new file mode 100644 index 0000000..3a0bb0f --- /dev/null +++ b/pytest/tests/yaml_files/test_simple.yaml @@ -0,0 +1,8 @@ +# test_simple.yaml +ok: + sub1: sub1 + +hello: + world: world1 + some: some + diff --git a/python/pytest/README.md b/python/pytest/README.md deleted file mode 100644 index 4993760..0000000 --- a/python/pytest/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Python Testing with Pytest - - -## References -https://github.com/jashburn8020/python-testing-with-pytest - diff --git a/python/pytest/unittest_mock/README.md b/python/pytest/unittest_mock/README.md deleted file mode 100644 index 22f62ee..0000000 --- a/python/pytest/unittest_mock/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Python Mock Object Library - - -## References - * https://docs.python.org/dev/library/unittest.mock.html - * https://pypi.org/project/pytest-mock/ - diff --git a/python/pytest/unittest_mock/examples/README.md b/python/pytest/unittest_mock/examples/README.md deleted file mode 100644 index 65afe60..0000000 --- a/python/pytest/unittest_mock/examples/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Examples - diff --git a/python/pytest/unittest_mock/examples/patch_examples/my_class.py b/python/pytest/unittest_mock/examples/patch_examples/my_class.py deleted file mode 100644 index 91a71ac..0000000 --- a/python/pytest/unittest_mock/examples/patch_examples/my_class.py +++ /dev/null @@ -1,34 +0,0 @@ - -# https://kimsereylam.com/python/2021/03/19/how-to-use-patch-in-python-unittest.html - -class MyClass: - my_attribute = 1 - - def __init__(self): - self.value = 2 - - def do_something(self): - return f"hello {self.value}" - -def get_value(): - o = MyClass() - return o.value - -print( get_value() ) - -def do_sth(): - o = MyClass() - return o.do_something() - -print( do_sth() ) - -## TEST -from unittest.mock import patch - -def test_myclass(): - with patch("my_class.MyClass") as mock: - mock.return_value.value = "hello" - print(get_value()) - - - diff --git a/python/pytest/unittest_mock/examples/week05-testing/README.md b/python/pytest/unittest_mock/examples/week05-testing/README.md deleted file mode 100644 index 2e01f5c..0000000 --- a/python/pytest/unittest_mock/examples/week05-testing/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# mock, pytests - -## Running pytests -``` -cd $HOME/repositories/mxochicale/code/python/pytest/unittest_mock/examples/week05-testing -conda activate simpleVE - - -pytest test_times.py -s -pytest test_times.py -m test_negative_time_range -pytest -k test_generic_case -v - -pytest test_times.py -v -``` \ No newline at end of file