0

I am trying to write unit test for function calling system process os.popen.

Function:

def change_from(lineTag):
    Lines = []
    command = "ls -al"
    lines = os.popen(command).readlines()
    for line in lines:
        if lineTag in line:
            temp = line.split(":")
            Lines.append(temp[1])
    return Lines

Initialy I just wrote a simple test:

def test_change_from(self):
    lineTag = "undocumented_line:"
    Lines = ['43', '45', '47', '50', '51', '52', '53', '54', '55']
    self.assertEqual(change_from(lineTag), Lines)

It passed, but gave me warning:

 ResourceWarning: unclosed file <_io.TextIOWrapper name=3 encoding='UTF-8'>
  lines = os.popen(command).readlines()

So I tried using Mock library to "simulate" os process:

Test:

import unittest
import os
from mock import *

@patch('os.popen')
    def test_change_from(self, mock_subproc_popen):
        mock_subproc_popen.return_value = Mock(communicate=('ouput', 'error'), returncode=0)
        lineTag = "undocumented_line:"
        Lines = ['43', '45', '47', '50', '51', '52', '53', '54', '55']
        self.assertEqual(change_from(lineTag), Lines)

And I am getting error while running test:

    for line in lines:
TypeError: 'Mock' object is not iterable

How could I correctly mock the os.popen process to have test passing without warnings?

Lin Du
  • 88,126
  • 95
  • 281
  • 483
Wojtas.Zet
  • 636
  • 2
  • 10
  • 30

1 Answers1

0

Here is the unit test solution:

main.py:

import os


def change_from(lineTag):
    Lines = []
    command = "ls -al"
    lines = os.popen(command).readlines()
    for line in lines:
        if lineTag in line:
            temp = line.split(":")
            Lines.append(temp[1])
    return Lines

test_main.py:

import unittest
from main import change_from, os
from unittest.mock import patch


class TestMain(unittest.TestCase):

    def test_change_from(self):
        with patch.object(os, 'popen') as mock_popen:
            Lines = ['43', '45', '47', '50', '51', '52', '53', '54', '55']
            mock_popen().readlines.return_value = Lines
            lineTag = "undocumented_line:"
            self.assertEqual(change_from(lineTag), [])
            mock_popen.assert_called_with('ls -al')
            mock_popen().readlines.assert_called_once()


if __name__ == '__main__':
    unittest.main()

Unit test result with coverage report:

.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Name                                      Stmts   Miss  Cover   Missing
-----------------------------------------------------------------------
src/stackoverflow/58165140/main.py           10      2    80%   10-11
src/stackoverflow/58165140/test_main.py      12      0   100%
-----------------------------------------------------------------------
TOTAL                                        22      2    91%

Source code: https://github.com/mrdulin/python-codelab/tree/master/src/stackoverflow/58165140

Lin Du
  • 88,126
  • 95
  • 281
  • 483