Welcome to systemfixtures’s documentation!

Overview

A collection of Python fixtures to fake out various system resources (processes, users, groups, etc.).

Each fake resource typically behaves as an “overlay” on the real resource, in that it can be programmed with fake behavior for a set of inputs, but falls back to the real behavior for the rest. See the examples below for more information.

The implementation is mostly built on top of the basic MonkeyPatch fixture.

Examples

Users

The FakeUsers fixture lets you add fake system users, that do not exist for real, but behave the same as real ones:

>>> import pwd

>>> from systemfixtures import FakeUsers

>>> users = FakeUsers()
>>> users.setUp()

>>> pwd.getpwnam("foo")
Traceback (most recent call last):
...
KeyError: 'getpwnam(): name not found: foo'

>>> users.add("foo", 123)
>>> info = pwd.getpwnam("foo")
>>> info.pw_uid
123
>>> users.cleanUp()

Groups

The FakeGroups fixture lets you add fake system groups, that do not exist for real, but behave the same as real ones:

>>> import grp

>>> from systemfixtures import FakeGroups

>>> groups = FakeGroups()
>>> groups.setUp()

>>> grp.getgrnam("foo")
Traceback (most recent call last):
...
KeyError: 'getgrnam(): name not found: foo'

>>> groups.add("foo", 123)
>>> info = grp.getgrnam("foo")
>>> info.gr_gid
123

>>> groups.cleanUp()

Filesystem

The FakeFilesystem fixture lets you add a temporary directory as filesystem “overlay”. You can declare certain paths as belonging to the overlay, and filesystem APIs like open(), os.mkdir(), os.chown(), os.chmod() and os.stat() will be transparently redirected to act on the temporary directory instead of the real filesystem path:

>>> import os
>>> import tempfile

>>> from systemfixtures import FakeFilesystem

>>> filesystem = FakeFilesystem()
>>> filesystem.setUp()

Trying to create a directory under the root one will fail, since we are running as unprivileged user:

>>> os.mkdir("/foo")
Traceback (most recent call last):
...
PermissionError: [Errno 13] Permission denied: '/foo'

However, if we add the directory path to the fake filesystem, it will be possible to create it as overlay directory:

>>> filesystem.add("/foo")
>>> os.mkdir("/foo")
>>> os.path.isdir("/foo")
True

The overlay directory actually lives under the temporary tree of the fake filesystem fixture:

>>> filesystem.root.path.startswith(tempfile.gettempdir())
True
>>> os.listdir(filesystem.root.path)
['foo']

It’s possible to operate on the overlay directory as if it was a real top-level directory:

>>> with open("/foo/bar", "w") as fd:
...    fd.write("Hello world!")
12
>>> with open("/foo/bar") as fd:
...    fd.read()
'Hello world!'
>>> os.listdir("/foo")
['bar']

It’s possible to change the ownership of files in the overlay directory, even without superuser priviliges:

>>> os.chown("/foo/bar", 0, 0)
>>> os.chmod("/foo/bar", 0o600)
>>> info = os.stat("/foo/bar")
>>> info.st_uid, info.st_gid
(0, 0)
>>> oct(info.st_mode)
'0o100600'

>>> filesystem.cleanUp()

Network

The FakeNetwork fixture is simply fixture-compatible adapter of the requests-mock package, which provides facilities to stub out responses from the requests package. For further details see the official documentation.

>>> import requests

>>> from systemfixtures import FakeNetwork

>>> network = FakeNetwork()
>>> network.setUp()

>>> network.get("http://test.com", text="data")  
<requests_mock.adapter._Matcher object at ...>
>>> response = requests.get("http://test.com")
>>> response.text
'data'

>>> network.cleanUp()

Time

The FakeTime fixture is simply fixture-compatible adapter of the fakesleep package, which provides facilities to stub out the API of time package from the standard library. See the external documentation

>>> import time

>>> from systemfixtures import FakeTime

>>> fake_time = FakeTime()
>>> fake_time.setUp()

>>> stamp1 = time.time()
>>> time.sleep(1)
>>> stamp2 = time.time()

Since sleep() and time() are fake, we get exactly 1.0:

>>> stamp2 - stamp1
1.0

>>> fake_time.cleanUp()

Processes

The FakeProcesses fixture lets you fake out processes spawed with subprocess.Popen, and have custom Python code be executed instead.

You can both override available system executables, or add new ones are not available on the system:

>>> import io
>>> import subprocess

>>> from systemfixtures import FakeProcesses

>>> processes = FakeProcesses()
>>> processes.setUp()

>>> subprocess.check_output(["uname"])
b'Linux\n'

>>> def uname(proc_args):
...     return {"stdout": io.BytesIO(b"Darwin\n")}

>>> processes.add(uname, name="uname")
>>> processes.uname  
<function uname at ...>

>>> subprocess.check_output(["uname"])
b'Darwin\n'

>>> def foo(proc_args):
...     return {"stdout": io.BytesIO(b"Hello world!")}

>>> processes.add(foo, name="foo")
>>> subprocess.check_output(["foo"])
b'Hello world!'

Some stock fake processes are provided as well:

wget

>>> from systemfixtures.processes import Wget

>>> processes.add(Wget())
>>> processes.wget.locations["http://foo"] = b"Hello world!"

>>> subprocess.check_output(["wget", "-O", "-", "http://foo"])
b'Hello world!'

systemctl

>>> from systemfixtures.processes import Systemctl

>>> processes.add(Systemctl())

>>> try:
...    subprocess.check_output(["systemctl", "is-active", "foo"])
... except subprocess.CalledProcessError as error:
...     error.output
b'inactive\n'

>>> subprocess.check_call(["systemctl", "start", "foo"])
0
>>> subprocess.check_output(["systemctl", "is-active", "foo"])
b'active\n'
>>> subprocess.check_call(["systemctl", "stop", "foo"])
0

>>> processes.systemctl.actions["foo"]
['start', 'stop']

dpkg

>>> from systemfixtures.processes import Dpkg

>>> processes.add(Dpkg())
>>> subprocess.check_call(["dpkg", "-i", "foo_1.0-1.deb"])
0
>>> processes.dpkg.actions["foo"]
['install']
>>> processes.cleanUp()

Threads

The FakeThreads fixture lets you fake out threads spawed with the threading module, and partially simulate their behavior in a synchronous way:

>>> import threading

>>> from systemfixtures import FakeThreads

>>> threads = FakeThreads()
>>> threads.setUp()

>>> calls = [None]
>>> thread = threading.Thread(target=calls.pop)
>>> thread.name
'fake-thread-0'

>>> thread.start()
>>> calls
[]
>>> thread.join()
>>> thread.isAlive()
False

It’s also possible to simulate a hung thread:

>>> threads.hang()
>>> thread = threading.Thread()
>>> thread.name
'fake-thread-1'
>>> thread.start()
>>> thread.join(timeout=60)  # This returns immediately
>>> thread.isAlive()
True

Executables

The FakeExecutable fixture lets you create temporary Python scripts that can be configured to mimic the behavior of some real executable (for instance listening to a port or emitting certain output):

>>> import stat
>>> from systemfixtures import FakeExecutable

>>> executable = FakeExecutable()
>>> executable.setUp()

>>> bool(os.stat(executable.path).st_mode & stat.S_IXUSR)
True

>>> executable.out('hello')
>>> subprocess.check_output([executable.path])
b'hello\n'