Skip to content

Testing

paarongiroux edited this page May 29, 2019 · 5 revisions

Testing concrete drivers with pytest

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.

Mocking the spice module

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

Using propertymock and patch to avoid reading from labels

A concrete driver should be using the various mix-ins to extract the information for its attributes. 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 return 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'