Question

I've inherited some python code that writes a new '/etc/udev/rules.d' mapping file and then makes a subprocess call to udev to have it refresh its devices list:

call(['/sbin/udevadm', 'trigger', '--action=change'])

The trigger call is necessary as we need the mapping to update without wanting to unplug and plug back in the device being mapped. My problem is the 'call' line was removed at one point, causing non-obvious side-effects in other parts of the program and so was not caught.

My usual way of fixing something like this is to throw a unit-test on this method (which writes the mapping file and calls trigger) to enforce the expected behaviour, but this behaviour seems outside of the realm of unit-testing. It's a system call, not to mention udevadm trigger requires sudo access. I can't figure out how/what to mock out in this instance.

I considered using the pyudev library as I saw that it can mock certain behaviour of udev, but it doesn't look like it can mock the trigger behaviour (or even access it for that matter).

Short of throwing a big "#DO NOT DELETE THIS LINE EVER!" above the "call" line, is there anything I can do here to prevent this from being removed in the future? "DO NOT DELETE" lines are easily ignored say a year from now when no one has a clue why it's there.

Était-ce utile?

La solution

Here's what I decided to do in this instance, if anyone disagrees with my answer please chime in!

It's a two parter.

First I wrapped the call I was concerned about and switched the straight 'call' to this in my code.

class UdevWrapper:
    def udevadm_trigger(self):
        call(['/sbin/udevadm', 'trigger', '--action=change'])

(The class contains more than this, just simplified here for clarity)

Next I mocked out the wrapper method and tested to ensure that it was called

@patch.object(utils.UdevWrapper,'udevadm_trigger')
def test_trigger_called(self,mock_udevadm_trigger):
    mock_udevadm_trigger.return_value = True

    # name changed for clarity
    ClassWhereTriggerCalled.func()
    assert mock_udevadm_trigger.called

Doing it this way was inspired by reading about Behaviour Driven Development. The whole idea of BDD is brand new to me so I'm not sure stylistically/functionally how BDD proponents will feel about my solution but it does what I wanted - something obviously breaks (my test) if this line of code is removed in the future.

I plan on switching it to use monkeypatch in the future so that I can create a stub function that can check state (the order in which trigger is called is also important). However the principle remains the same:

  1. Wrap in a method that can be mocked
  2. Write test to ensure correct functional behaviour
  3. Mock wrapped method
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top