Skip to content

Commit

Permalink
Merge pull request #56 from mxochicale/54-pytest
Browse files Browse the repository at this point in the history
54 - created pytest path, tidies scripts and READMEs and updates eVE.yaml #54
  • Loading branch information
mxochicale authored Dec 23, 2023
2 parents c3d987f + 4028e78 commit aef090e
Show file tree
Hide file tree
Showing 17 changed files with 208 additions and 84 deletions.
2 changes: 1 addition & 1 deletion pyVEs/bareVE.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
37 changes: 26 additions & 11 deletions pyVEs/eVE.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions pytest/README.md
Original file line number Diff line number Diff line change
@@ -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)).
<img src="fig-testing-workflows.png" height="200">

```
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/

Binary file added pytest/fig-testing-workflows.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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
Expand Down
33 changes: 33 additions & 0 deletions pytest/tests/test_my_class.py
Original file line number Diff line number Diff line change
@@ -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"



49 changes: 49 additions & 0 deletions pytest/tests/week05-testing/README.md
Original file line number Diff line number Diff line change
@@ -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 = <dict_keyiterator object at 0x7f32b1822390>
> 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
`
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -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]
Expand Down
59 changes: 59 additions & 0 deletions pytest/tests/yaml_files/conftest.py
Original file line number Diff line number Diff line change
@@ -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."""
8 changes: 8 additions & 0 deletions pytest/tests/yaml_files/test_simple.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# test_simple.yaml
ok:
sub1: sub1

hello:
world: world1
some: some

6 changes: 0 additions & 6 deletions python/pytest/README.md

This file was deleted.

7 changes: 0 additions & 7 deletions python/pytest/unittest_mock/README.md

This file was deleted.

2 changes: 0 additions & 2 deletions python/pytest/unittest_mock/examples/README.md

This file was deleted.

34 changes: 0 additions & 34 deletions python/pytest/unittest_mock/examples/patch_examples/my_class.py

This file was deleted.

14 changes: 0 additions & 14 deletions python/pytest/unittest_mock/examples/week05-testing/README.md

This file was deleted.

0 comments on commit aef090e

Please sign in to comment.