How to unit test program interacting with block devices
<p>I have a program that interacts with and changes block devices (/dev/sda and such) on linux. I'm using various external commands (mostly commands from the fdisk and GNU fdisk packages) to control the devices. I have made a class that serves as the interface for most of the basic actions with block devices (for information like: What size is it? Where is it mounted? etc.)</p>

<p>Here is one such method querying the size of a partition:</p>

<pre><code>def get_drive_size(device):
    """Returns the maximum size of the drive, in sectors.

    :device the device identifier (/dev/sda and such)"""

    query_proc = subprocess.Popen(["blockdev", "--getsz", device], stdout=subprocess.PIPE) 
    #blockdev returns the number of 512B blocks in a drive
    output, error = query_proc.communicate()
    exit_code = query_proc.returncode
    if exit_code != 0:
        raise Exception("Non-zero exit code", str(error, "utf-8")) #I have custom exceptions, this is slight pseudo-code

    return int(output) #should always be valid
</code></pre>

<p>So this method accepts a block device path, and returns an integer. The tests will run as root, since this entire program will end up having to run as root anyway.</p>

<p>Should I try and test code such as these methods? If so, how? I could try and create and mount image files for each test, but this seems like a lot of overhead, and is probably error-prone itself. It expects block devices, so I cannot operate directly on image files in the file system.</p>

<p>I could try mocking, as some answers suggest, but this feels inadequate. It seems like I start to test the implementation of the method, if I mock the Popen object, rather than the output. Is this a correct assessment of proper unit-testing methodology in this case?</p>

<p>I am using python3 for this project, and I have not yet chosen a unit-testing framework. In the absence of other reasons, I will probably just use the default unittest framework included in Python.</p>

<p>You should look into the mock module (I think it's part of the unittest module now in Python 3).</p>

<p>It enables you to run tests without the need to depened in any external resources while giving you control over how the mocks interact with your code.</p>

<p>I would start from the docs in <a href="http://www.voidspace.org.uk/python/mock/" rel="nofollow">Voidspace</a></p>

<p>Here's an example:</p>

<pre><code>import unittest2 as unittest
import mock

class GetDriveSizeTestSuite(unittest.TestCase):

  @mock.patch('path/to/original/file.subprocess.Popen')
  def test_a_scenario_with_mock_subprocess(self, mock_popen):
    mock_popen.return_value.communicate.return_value = ('Expected_value', '')
    mock_popen.return_value.returncode = '0'
    self.assertEqual('expected_value', get_drive_size('some device'))
</code></pre>

python unit-testing