diff --git a/README.md b/README.md index 4b70231..10dcc13 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/). For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter). -_1756 TILs and counting..._ +_1757 TILs and counting..._ See some of the other learning resources I work on: @@ -1044,6 +1044,7 @@ If you've learned something here, support my efforts writing daily TILs by - [Access Most Recent Return Value In REPL](python/access-most-recent-return-value-in-repl.md) - [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md) - [Check If Package Is Installed With Pip](python/check-if-package-is-installed-with-pip.md) +- [Control Passing Of Time In Tests](python/control-passing-of-time-in-tests.md) - [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md) - [Dunder Methods](python/dunder-methods.md) - [Easy Key-Value Aggregates With defaultdict](python/easy-key-value-aggregates-with-defaultdict.md) diff --git a/python/control-passing-of-time-in-tests.md b/python/control-passing-of-time-in-tests.md new file mode 100644 index 0000000..90e8f0a --- /dev/null +++ b/python/control-passing-of-time-in-tests.md @@ -0,0 +1,40 @@ +# Control Passing Of Time In Tests + +While it is nice to be able to write pure functional code, our software still +lives in the real world and may have to relate to or depend on the passing of +time. In order to test this kind of code, we need time to behave in a reliable, +deterministic way. One of the best ways to create a testing environment where +that is true is to bring in tooling that hijacks time. + +The [`freezegun` module](https://github.com/spulec/freezegun) is a great tool +for that job. We can use it to freeze time at a specific testable point, advance +time a specific amount, and much more. + +Here is an example from the tests for [my CLI-based time tracking +app](https://github.com/jbranchaud/py-vmt/blob/acb26e4840279d936a12f16c505ca7e75e9a6d20/tests/src/py_vmt/test_cli.py#L21) +where I freeze time before starting a session. That gives me a chance to assert +about the exact start time that is output by the command. Then I can advance +time a little and assert that the `status` command outputs the correct thing. + +```python +import datetime +from freezegun import freeze_time + +# some other test setup omitted ... + +initial_datetime = datetime.datetime( + 2026, 3, 14, 15, 5, 11, 0, datetime.timezone.utc +) +with freeze_time(initial_datetime) as frozen_datetime: + # start a session + start_result = runner.invoke(cli, ["start", "my-project"]) + output = "Started tracking 'my-project' at 10:05AM" + assert output in start_result.output + + frozen_datetime.tick(delta=datetime.timedelta(minutes=30)) + + # check status + status_result = runner.invoke(cli, ["status"]) + output = "Tracking 'my-project' for 30m (since 10:05AM)" + assert output in status_result.output +```