-
Notifications
You must be signed in to change notification settings - Fork 33
Testing
First ensure that you have a version of pytest installed. This can be easily installed into a conda environment (the environment used to build ale might be a good option) by activating the environment and running the command:
conda install -c conda-forge pytest
You may run the pytest command either on the tests/pytests directory to run tests from all files within the directory, or on an individual file within the directory with the following commands:
# This will run all ale pytests
pytest <path>/<to>/<repository>/tests/pytests
# This will run tests only within the test_util.py file
pytest <path>/<to>/<repository>/tests/pytests/test_util.py
In a concrete driver test, all functions or attributes declared in the concrete driver class itself should be tested. You don't need to worry about testing the inherited functions or attributes since they will be tested elsewhere. The python file itself should be named 'test_<file_to_be_tested>.py' (i.e. test_cassini_drivers.py). Each individual test should be named 'test_<attribute_name>' (i.e. test_instrument_id).
In order to avoid declaring a new driver in each test, drivers should be passed into the tests as fixtures like so:
@pytest.fixture
def driver():
return CassiniIssPds3LabelNaifSpiceDriver("")
def test_instrument_id(driver):
assert ...
Since we are not reading from a label at any point during this test, we can just pass in an empty string into the driver.
We also have a SimpleSpice class and a get_mockkernels function to avoid having to retrieve information from spice kernels. The following lines of code will create a mock the spice module to avoid the use of spice kernels.
from conftest import SimpleSpice, get_mockkernels
simplespice = SimpleSpice()
data_naif.spice = simplespice
<file_to_be_tested_>.spice = simplespice
label_pds3.spice = simplespice
from ale.drivers.<file_to_be_tested_> import <Driver_to_be_tested>
<Driver_to_be_tested>.metakernel = get_mockkernels
A concrete driver should be using the various mix-ins to extract the information for its attributes whenever possible. So instead of having a label that we test on, we can use property mock and/or patch to change returned values so that we don't have to read from anything during these tests.
If we need to test multiple return values of the same mix-in property in the same test, the following format works best:
def test_target_name(driver):
with patch('ale.base.label_pds3.Pds3Label.target_name', new_callable=PropertyMock) as mock_target_name:
mock_target_name.return_value = '4 VESTA'
assert driver.target_name == 'VESTA'
mock_target_name.return_value = 'VESTA'
assert driver.target_name == 'VESTA'
If only one return value for any give in mix-in is needed in a test, we can use the following format in order to improve readability:
@patch('ale.base.label_pds3.Pds3Label.instrument_host_id', 'DAWN')
def test_spacecraft_name(driver):
assert driver.spacecraft_name == 'DAWN'
The previous format also works for mocking the return value of several different properties (see example below). This is much better than the alternative of nesting several different with branches.
@patch('ale.base.label_pds3.Pds3Label.instrument_id', 1)
@patch('ale.base.label_pds3.Pds3Label.filter_number', 2)
def test_instrument_id(driver):
assert driver.instrument_id == 'DAWN_1_FILTER_2'
In the case that the driver being tested must read directly from the label, we can use patch.dict to mock certain values in the label attribute like so:
with patch.dict(driver.label, {'INSTRUMENT_ID':'123'}) as f:
assert ...