I have some code that checks that a file is treated differently if some time has passed. In other words, it reacts if the modification time is more than it was before. The code uses os.stat(filename)[stat.ST_MTIME] to do this. The challenge was to mock os.stat so that I can pretend some time has passed without having to wait. Here was my first solution which made running many tests sloooow:


def test_file_changed(self):
    filename = 'foo.txt'
    expect_timestamp = int(time.time())
    ...run the unit test with 'expect_timestamp'...
    time.sleep(1)
    expect_timestamp += 1
    ...run the unit test with 'expect_timestamp'...

So, here's how I mock os.stat to avoid having to do the time.sleep(1) in the test:


def test_file_changed(self):
    filename = 'foo.txt'
    expect_timestamp = int(time.time())
    ...run the unit test with 'expect_timestamp'...
    import os
    from posix import stat_result
    def fake_stat(arg):
        if arg == filename:
            faked = list(orig_os_stat(arg))
            faked[stat.ST_MTIME] = faked[stat.ST_MTIME] + 1
            return stat_result(faked)
        else:
            return orig_os_stat(arg)
    orig_os_stat = os.stat
    os.stat = fake_stat

    expect_timestamp += 1
    ...run the unit test with 'expect_timestamp'...

I hope this helps someone else who's trying to do the same. It took me some time to figure out that os.stat is used by lots of various sub routines in the os module so it's important to only mock the relevant argument otherwise you might get unexpected problems.

Comments

Martin Brochhaus

Let's assume you only want to find out the creation date...

Why not create a new method

get_cr_date(self, filename):
return os.stat(filename).st_crdate

Now you can easily mock that method if you use Mock for example:

@patch("yourmodule.yourclass.get_cr_date")
def test_should_do_sthg_special_when_x_minutes_passed(self, mock_get_cr_date):
mock_get_cr_date.return_value = whatever
do your test here...

Peter Bengtsson

I'm not entirely sure I understand but I'm sure there are other ways to do it. I think the trick I managed to point out was twofold. That you have to only do it for the file you expect to mock and that you need to use posix.stat_result()

Alec Munro

We've hit things like this a lot. What we decided to do was make the os module a dependency of our object.

So:

def __init__(self, ..., os_mod=os):
...
self.os = os_mod

def method_that_uses_os:
self.os.stat(...)

This can then easily be injected when creating your test objects, and used in your tests (we use mocker for mocking, but the pattern applies pretty much universally).

def test_method_that_uses_os(self):
m_os = self.mocker.mock()
test_obj = SomeObj(..., os_mod=m_os)

m_os.stat(...)

self.mocker.replay()
test_obj.method_that_uses_os()

The only particular annoyance to this method is that the dependency list can get quite long. But it does keep things very explicit. :)

Virgil Dupras

I ran into a need for this just today and I remembered I had read about your post. I thought you might be interested by the approach I took (I feel it's a little more generic and elegant than your solution):

https://hardcoded.svn.beanstalkapp.com/lib/hsutil/trunk/testcase.py

You make your testcases subclass this TestCase, then just call self.mock_osstat('mypath', st_mtime=42)

cvan

Good read! Stumbled upon this, as I was mocking `os.stat(path).st_mtime` in a Django view. I found this to work quite nicely:

    @mock.patch('site.views.os')
    def test_package_etag(self, os_mock):
        os_mock.stat.return_value = mock.Mock(st_mtime=42)

Cheers!

Your email will never ever be published.

Previous:
"Frank Zappa: The Biography" by Barry Miles October 30, 2009 Books
Next:
Those Crazy Chinese November 16, 2009 Django
Related by category:
How I run standalone Python in 2025 January 14, 2025 Python
get in JavaScript is the same as property in Python February 13, 2025 Python
How to resolve a git conflict in poetry.lock February 7, 2020 Python
Best practice with retries with requests April 19, 2017 Python
Related by keyword:
To assert or assertEqual in Python unit testing February 14, 2009 Python
Careful with your assertRaises() and inheritance of exceptions April 10, 2013 Python
Mocking DBRefs in Mongoose and nodeunit April 14, 2011 MongoDB, JavaScript
String comparison function in Python (alpha) December 22, 2007 Python