Search code examples
pythonmockingbddpython-behave

How to Mock In a BDD Steps File


I would like to mock behaviour of the os.path.exists method so that I can verify if my script behaves correctly when os.path.exists reports that the file/folder does not exist.

@when("Service starts with input file that does not exist")
def step_impl(context):
    """
    :type context: behave.runner.Context
    """
    json_file_path = "fake_file_path"
    mock_os_path = mock.Mock()
    mock_os_path.exists.return_value = False

    context.returncode = dicom_send_service.launch(json_file_path)

    mock_os_path.exists.assert_called_once_with(json_file_abspath)

How can I inject the mock into my script? I tried to use the

@mock.patch("mymodule.os.path")
@when("Service starts with input file that does not exist")
def step_impl(context, mock_os_path):

However, when I run the method python returns:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/behave/model.py", line 1456, in run
    match.run(runner.context)
  File "/usr/local/lib/python2.7/dist-packages/behave/model.py", line 1903, in run
    self.func(context, *args, **kwargs)
TypeError: step_impl() takes exactly 2 arguments (1 given)

As you can see the step_impl method expected 2 arguments based on the declaration, but BDD called it with only 1 (the context value) and the mock annotation was not picked up.

Here is the code that I am testing:

import os

def validate(json_file_path):
    """Method which validates the JSON file, an error message is returned if the file fails verification.

        json_file_path -- the path to the file with the message configuration details"""
    if not os.path.exists(json_file_path):
        return "Could not find file at path {0}".format(json_file_path)
    ...
    return ""

def launch(json_file_path):
    error_message = valid(json_file_path)
    if error_message:
        print(error_message)
        return 1

Solution

  • So to answer my own question, you have to use the with mock.patch syntax:

    with mock.patch('name of thing to mock') as name_of_mock:
    

    So my example above would become:

    @when("Service starts with input file that does not exist")
    def step_impl(context):
        """
        :type context: behave.runner.Context
        """
        json_file_path = "fake_file_path"
    
        # This is where the magic happens
        with mock.patch ('os.path') as mock_os_path:
    
            mock_os_path.exists.return_value = False
    
            context.returncode = dicom_send_service.launch(json_file_path)
    
            mock_os_path.exists.assert_called_once_with(json_file_abspath)
    

    I've tested it out and it works like a charm. Much easier than using other mocking frameworks like Mockito or Powermock from Java.