0

I am trying to unit test a script that reads a requirements.txt file and goes through each line installing it with pip. However it my unit test it doesn't seem to enter the loop because the self.requirements is empty. I tried setting it to self.setupvirtualenv.requirements to ['foobar'] but this doesn't work.

My question is how can I mock the self.requirements list so that it enters the for loop and the subprocess.call gets called?

def install_requirements(self):
    self.requirements = open(self.requirements_path, 'r').readlines()
    for req in self.requirements:
        req = req.strip()
        pip_command = r'pip install {}'.format(req)
        subprocess.call(pip_command, shell=True)

My unit test:

import setupvirualenv
import unittest
from mock import patch, Mock

def test_install_requirements(self, mock_open, mock_subprocess_call):
    self.setupvirtualenv.requirements = ['foobar']
    self.setupvirtualenv.install_requirements()
    mock_open.assert_called_once_with('foobar', 'r')
    mock_subprocess_call.assert_called_once() 

The failure message

AssertionError: Expected 'call' to have been called once. Called 0 times.
Sneftel
  • 40,271
  • 12
  • 71
  • 104
Xaztek
  • 15
  • 4

1 Answers1

0

You should mock the open call, so that readlines() returns the dummy data.

It looks as though you may be missing the patch decorators from your test method. I have included what they should look like below.

from mock import patch

@patch('setupvirtualenv.subprocess.call')
@patch('setupvirtualenv.open')
def test_install_requirements(self, mock_open, mock_subprocess_call):
    # Mock the call to open() and the return value from readlines()
    mock_open.return_value.readlines.return_value = ['foobar\n']
    self.setupvirtualenv.install_requirements()
    mock_open.assert_called_once_with('foobar', 'r')
    mock_subprocess_call.assert_called_once() 
Will Keeling
  • 22,055
  • 4
  • 51
  • 61
  • Thanks a lot for your help! I already had the decorators, but forgot to copy-paste them. I needed `mock_open.return_value.readlines.return_value = ['foobar\n']` to get it to work. – Xaztek Apr 22 '18 at 13:55