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 0000000..216f145 Binary files /dev/null and b/pytest/fig-testing-workflows.png differ diff --git a/python/pytest/unittest_mock/examples/patch_examples/example00.py b/pytest/tests/test_mocker_sums.py similarity index 91% rename from python/pytest/unittest_mock/examples/patch_examples/example00.py rename to pytest/tests/test_mocker_sums.py index 7ec52d3..863a94e 100644 --- a/python/pytest/unittest_mock/examples/patch_examples/example00.py +++ b/pytest/tests/test_mocker_sums.py @@ -1,5 +1,5 @@ import pytest -from mock import call +from mock import call #pip install pytest-mock # References # https://stackoverflow.com/questions/53434986/using-mocker-to-patch-with-pytest diff --git a/pytest/tests/test_my_class.py b/pytest/tests/test_my_class.py new file mode 100644 index 0000000..a356ba4 --- /dev/null +++ b/pytest/tests/test_my_class.py @@ -0,0 +1,33 @@ +from unittest.mock import patch +# https://kimsereylam.com/python/2021/03/19/how-to-use-patch-in-python-unittest.html + +class MyClass: + my_class_attribute = 1 + + def __init__(self): + self.instance_attribute_value = 2 + + def method_do_something(self): + return f"hello {self.instance_attribute_value}" + + +def test_instance_attribute_value(): + o = MyClass() + with patch.object(o, "instance_attribute_value", return_value=33): + print(f"PRINT o.instance_attribute_value: {o.instance_attribute_value()}") + assert o.instance_attribute_value() is 33 + +def test_path_my_class_attribute(): + o = MyClass() + with patch.object(o, "my_class_attribute", return_value=3): + print(f"PRINT o.my_class_attribute: {o.my_class_attribute()}") + assert o.my_class_attribute() is 3 + +def test_patch_method_do_sth(): + o = MyClass() + with patch.object(o, "method_do_something", return_value="hello"): + print(f"PRINT: o.method_do_something(): {o.method_do_something()}") + assert o.method_do_something() is "hello" + + + diff --git a/pytest/tests/week05-testing/README.md b/pytest/tests/week05-testing/README.md new file mode 100644 index 0000000..7948d35 --- /dev/null +++ b/pytest/tests/week05-testing/README.md @@ -0,0 +1,49 @@ +# week05-testing + +## issues +``` + +pytest test_times.py +=============================================================== test session starts ================================================================ +platform linux -- Python 3.11.7, pytest-7.4.3, pluggy-1.3.0 +rootdir: /home/mxochicale/repositories/mxochicale/code/pytest/tests/week05-testing +plugins: mock-3.12.0 +collected 7 items + +test_times.py ......F [100%] + +===================================================================== FAILURES ===================================================================== +_________________________________________________________________ test_iss_passes __________________________________________________________________ + + def test_iss_passes(): + with mock.patch("requests.get", new=mock.MagicMock(return_value=ISS_response())) as mymock: +> 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