---
# System prepended metadata

title: Testing tips & examples

---

# Testing tips & examples

## General philosophy
https://pythontesting.net/strategy/given-when-then-2/


## Unit testing API client code

### Mocking and testing "wrapper" code for an API client

Let's say we're using a Python package that provides client functionality for the [**Workflow Execution Service**]() API. This library includes the `WESClient` class that we can instantiate and use to interact with a WES server. However, if we have code that *wraps* and does something with the `WESClient` object, we want to be able to test just that added functionality (not the behavior of the `WESClient` class itself).


In this [example](https://github.com/Sage-Bionetworks/workflow-interop/blob/develop/wfinterop/wes/client.py#L62-L75), the wrapping method is actually part of an "adapter" class for the WES API; the `WESAdapter` is designed to pass and translate inputs/outputs to and from the `WESClient`, but through a more standard interface. So, we have a method called `GetServiceInfo()` that wraps `get_service_info()` from our client library. How do we test that `GetServiceInfo()` as behaving correctly?

```python
class WESAdapter(WESInterface):
    """
    Adapter class for the WES client functionality from the
    workflow-service library.
    :param wes_client:
    """
    _wes_client = None

    def __init__(self, wes_client):
        self._wes_client = wes_client

    def GetServiceInfo(self):
        return self._wes_client.get_service_info()
```

Here is the [test code](https://github.com/Sage-Bionetworks/workflow-interop/blob/develop/tests/test_wes.py#L62-L72) for the method. We instanstiate a `WESAdapter` object (`wes_adapter`), make an example request, and verify that the underlying `get_service_info()` method is called with the expected arguments.


```python    
def test_GetServiceInfo(self, mock_client_lib):
    mock_response = {}
    mock_client_lib.get_service_info.return_value = mock_response

    wes_adapter = WESAdapter(wes_client=mock_client_lib)
    test_args = {arg: '' for arg in    
                inspect.getargspec(WESClient.get_service_info)[0][1:]}
    test_response = wes_adapter.GetServiceInfo()

    mock_client_lib.get_service_info.assert_called_once_with(**test_args)
    assert test_response == mock_response
```

Some things to note:

+ We don't care about the output of `get_service_info()` — that's not what we're testing here. It's not obvious here, but the `mock_client_lib` variable is a `mock.Mock` object. This object includes the `return_value()` setter, which allows us to pre-define the output for any given method. In this case, we'll just set the response to be an empty dict.
+ Rather than manually defining test values for all the arguments to `get_service_info()`, we're a list comprehension to automatically assign a blank string `''` for any arguments captured by `inspect.getargspec()` — in hindsight, this might be problematic if any of the arguments expected non-string types...
+ But where does `mock_client_lib` actually come from? We'll get to that in a minute.

First, a brief digression on naming. This is totally a matter of personal preference, but I tend to use the `mock_` prefix to denote items that are part of my *state* (or "environment") for the test. I use `test_` to indicate the inputs and outputs of the test itself. Using the Given-When-Then structure from the article linked above, the test would look something like this:

```python    
def test_GetServiceInfo(self, mock_client_lib):
    # GIVEN a mock client library and a dummy response
    mock_response = {}
    mock_client_lib.get_service_info.return_value = mock_response

    # WHEN a WESAdapter object is initialized to wrap the client library
    # (I maybe should have named this 'test_wes_adapter'...)
    wes_adapter = WESAdapter(wes_client=mock_client_lib)
    
    # AND the GetServiceInfo method of the object is called with a
    # known set of arguments
    test_args = {arg: '' for arg in    
                inspect.getargspec(WESClient.get_service_info)[0][1:]}
    test_response = wes_adapter.GetServiceInfo()

    # THEN the client library should have been called with the expected
    # arguments
    mock_client_lib.get_service_info.assert_called_once_with(**test_args)
    
    # AND the response should match the expected dummy response
    assert test_response == mock_response
```

It's not a perfect naming convention by any means, so no need to be overly strict about it — but it can help to keep track of things.

Back to `mock_client_lib`...

For convenience and reuse across our tests, we can define a "fixture"[example](https://github.com/Sage-Bionetworks/workflow-interop/blob/develop/tests/conftest.py#L134-L139)

```python
@pytest.fixture()
def mock_client_lib(request):
    mock_wes_client = mock.Mock(name='mock WESClient')
    with mock.patch('wes_client.util.WESClient', 
                    autospec=True, spec_set=True):
        yield mock_wes_client
```

Mocking API responses:
[example](https://github.com/Sage-Bionetworks/workflow-interop/blob/develop/tests/conftest.py#L150-L161)

```python
@pytest.fixture(params=[None, 'workflow-service'])
def mock_wes_client(request):
    if request.param is None:
        mock_api_client = mock.Mock(name='mock SwaggerClient')
        with mock.patch.object(SwaggerClient, 'from_spec', 
            return_value=mock_api_client):
            yield mock_api_client
    else:
        mock_api_client = mock.Mock(name='mock WESAdapter')
        with mock.patch('wfinterop.wes.client.WESAdapter', 
                        autospec=True):
            yield mock_api_client
```
[example](https://github.com/Sage-Bionetworks/workflow-interop/blob/develop/tests/test_wes.py#L184-L194)

```python    
def test_get_service_info_direct(self, mock_wes_client):
    mock_service_info = {'workflow_type_versions': ['CWL', 'WDL']}

    mock_wes_client.GetServiceInfo.return_value = mock_service_info

    wes_instance = WES(wes_id='mock_wes', 
                       api_client=mock_wes_client)
    test_service_info = wes_instance.get_service_info()

    assert isinstance(test_service_info, dict) 
    assert test_service_info == mock_service_info
```
Testing business logic & monkeypatching
```python
def test_run_submission(mock_submission, 
                        mock_run_log,
                        mock_wes, 
                        monkeypatch):
    monkeypatch.setattr('wfinterop.orchestrator.get_submission_bundle', 
                        lambda x,y: mock_submission['mock_sub'])
    monkeypatch.setattr('wfinterop.orchestrator.update_submission', 
                        lambda w,x,y,z: None)

    monkeypatch.setattr('wfinterop.orchestrator.run_job', 
                        lambda **kwargs: mock_run_log)

    test_run_log = run_submission(queue_id='mock_queue',
                                  submission_id='mock_sub')

    assert test_run_log == mock_run_log
```