Machine Learning

Practical libraries of Python may never hear: Freezegun

That tests our code is an important and important part of the software life cycle. This is probably more important when discussing AI and ML programs, where the natural factor of uncertainty and HALLucinatory may be solved from the beginning.

And within this general test frame, the mobile test code is based on the day or current time can be a real head. How did you check out between midnight, counting related days (“2 hours ago”), or can you be added to the Leap of years or month-ends? Manually Mocketime Module can be beautiful and error.

If you've been arguing with this, you are not alone. But what if you can just stop … or even go your test?

That is exactly what the Frezegun library let you do. A good solution to a common test problem, but many Epython-experienced developers have never heard about it.

Freezegun Allows your Python tests to imitate certain times during laughter time, date, time, and Pendulum Python Module. It is easy to use but has a power to build decisive and reliable tests of a time of time.

Why is freezegun so useful?

  1. Determines. This primary benefit benefit is. A test consisting of time is fully predictable. DateTime.Now run () within frog block returns the same brightness, eliminating hazardous milisecland drug tests or rollovers Day during test testing.
  2. Easy. Compared with handbreading Publisher.now or to use Unitest.mockThe Freezen often cleans the most and requires a lower bioplate code, especially when changing time.
  3. The passage of time. Easily use certain days and times – ago, yet the future. This is important for evaluation cases, such as the end of the year, LEAPs, day savings, or simply confirming the logic bound in certain events.
  4. Equal period of time. Functions of related tests related (eg
  5. Tick ​​Tock. The freezegun allows the time to advance (“Tick”) from the ice minute within a test, ready for time testing, time, or sequence of time relying.

I hope, I have confirmed that the Freezegun can add significant addition to your Python tool box. Let's see works by looking at other sample codes abbreviations.

Setting up Dev

But before this, let us stop the facility of the effort. I use the miniconda for this, but you can use any tool that is familiar.

I am Windows User, but I usually grow using WSL2 personality with Windows, which I will do here.

All code I show should be effective under Windows or Unix-like programs.

# Create and activate a new dev environment
#
(base) $ conda create -n freezegun python=3.12 -y
(base) $ conda activate freezegun

Now, we can include libraries already needed.

(freezegun) $ pip install freezegun jupyter

I will be using Jobyter NeTebook to run my code. Following, type jupyter notebook in your order. You should see the Jusster brochure open in your browser. If that doesn't automatically, you may be able to see the information screefil after jupyter notebook command. Next to the floor, you will receive the URL to copy and paste on your browser to introduce the Jobyter textbook.

Your URL will be different with mine, but you should look like such a thing: –

Fast side: The code that I display in my examinations below make the most use of Python tighten command. If you have not fallen into this work before or do not make many spynical test, it is used to use testing if the condition is true, and if not, it suggests AssertionError.This helps to catch the issues during development and often used for abuse and ensure consideration in the code.

Example 1: The basic time of dictionary using decoration

The most common way of using the Freezegun is its decorator, @Freer_Time, that allows you to “Set up” some time for the day to test a variety of activities related to time.

import datetime
from freezegun import freeze_time

def get_greeting():
    now = datetime.datetime.now()
    print(f"  Inside get_greeting(), now = {now}") # Added print
    if now.hour < 12:
        return "Good morning!"
    elif 12 <= now.hour < 18:
        return "Good afternoon!"
    else:
        return "Good evening!"

# Test the morning greeting
@freeze_time("2023-10-27 09:00:00")
def test_morning_greeting():
    print("Running test_morning_greeting:")
    greeting = get_greeting()
    print(f"  -> Got greeting: '{greeting}'")
    assert greeting == "Good morning!"

# Test the evening greeting
@freeze_time("2023-10-27 21:30:00")
def test_evening_greeting():
    print("nRunning test_evening_greeting:")
    greeting = get_greeting()
    print(f"  -> Got greeting: '{greeting}'")
    assert greeting == "Good evening!"

# Run the tests
test_morning_greeting()
test_evening_greeting()
print("nBasic decorator tests passed!")

# --- Failure Scenario ---
# What happens if we don't freeze time?
print("n--- Running without freeze_time (might fail depending on actual time) ---")
def test_morning_greeting_unfrozen():
    print("Running test_morning_greeting_unfrozen:")
    greeting = get_greeting()
    print(f"  -> Got greeting: '{greeting}'")
    # This assertion is now unreliable! It depends on when you run the code.
    try:
        assert greeting == "Good morning!" 
        print("  (Passed by chance)")
    except AssertionError:
        print("  (Failed as expected - time wasn't 9 AM)")

test_morning_greeting_unfrozen()

And results.

Running test_morning_greeting:
  Inside get_greeting(), now = 2023-10-27 09:00:00
  -> Got greeting: 'Good morning!'

Running test_evening_greeting:
  Inside get_greeting(), now = 2023-10-27 21:30:00
  -> Got greeting: 'Good evening!'

Basic decorator tests passed!

--- Running without freeze_time (might fail depending on actual time) ---
Running test_morning_greeting_unfrozen:
  Inside get_greeting(), now = 2025-04-16 15:00:37.363367
  -> Got greeting: 'Good afternoon!'
  (Failed as expected - time wasn't 9 AM)

Example 2: Basic Time Shida using the status manager

Create “Block” of snow.

import datetime
from freezegun import freeze_time

def process_batch_job():
    start_time = datetime.datetime.now()
    # Simulate work
    end_time = datetime.datetime.now() # In reality, time would pass
    print(f"  Inside job: Start={start_time}, End={end_time}") # Added print
    return (start_time, end_time)

def test_job_timestamps_within_frozen_block():
    print("nRunning test_job_timestamps_within_frozen_block:")
    frozen_time_str = "2023-11-15 10:00:00"
    with freeze_time(frozen_time_str):
        print(f"  Entering frozen block at {frozen_time_str}")
        start, end = process_batch_job()
        
        print(f"  Asserting start == end: {start} == {end}")
        assert start == end
        print(f"  Asserting start == frozen time: {start} == {datetime.datetime(2023, 11, 15, 10, 0, 0)}")
        assert start == datetime.datetime(2023, 11, 15, 10, 0, 0)
        print("  Assertions inside block passed.")
        
    print("  Exited frozen block.")
    now_outside = datetime.datetime.now()
    print(f"  Time outside block: {now_outside} (should be real time)")
    # This assertion just shows time is unfrozen, value depends on real time
    assert now_outside != datetime.datetime(2023, 11, 15, 10, 0, 0)

test_job_timestamps_within_frozen_block()
print("nContext manager test passed!")

The result.

 Running test_job_timestamps_within_frozen_block:
 Entering frozen block at 2023-11-15 10:00:00
 Inside job: Start=2023-11-15 10:00:00, End=2023-11-15 10:00:00
 Asserting start == end: 2023-11-15 10:00:00 == 2023-11-15 10:00:00
 Asserting start == frozen time: 2023-11-15 10:00:00 == 2023-11-15 10:00:00
 Assertions inside block passed.
 Exited frozen block.
 Time outside block: 2025-04-16 15:10:15.231632 (should be real time)

 Context manager test passed!

Example 3: Tick progress with Tick

Imitating the time to pass in a decent period.

import datetime
import time
from freezegun import freeze_time

def check_if_event_expired(event_timestamp, expiry_duration_seconds):
    now = datetime.datetime.now()
    expired = now > event_timestamp + datetime.timedelta(seconds=expiry_duration_seconds)
    print(f"  Checking expiry: Now={now}, Event={event_timestamp}, ExpiresAt={event_timestamp + datetime.timedelta(seconds=expiry_duration_seconds)} -> Expired={expired}")
    return expired

# --- Manual ticking using context manager ---
def test_event_expiry_manual_tick():
    print("nRunning test_event_expiry_manual_tick:")

    with freeze_time("2023-10-27 12:00:00") as freezer:
        event_time_in_freeze = datetime.datetime.now()
        expiry_duration = 60
        print(f"  Event created at: {event_time_in_freeze}")

        print("  Checking immediately after creation:")
        assert not check_if_event_expired(event_time_in_freeze, expiry_duration)

        # Advance time by 61 seconds
        delta_to_tick = datetime.timedelta(seconds=61)
        print(f"  Ticking forward by {delta_to_tick}...")
        freezer.tick(delta=delta_to_tick)

        print(f"  Time after ticking: {datetime.datetime.now()}")
        print("  Checking after ticking:")
        assert check_if_event_expired(event_time_in_freeze, expiry_duration)

        print("  Manual tick test finished.")

# --- Failure Scenario ---
@freeze_time("2023-10-27 12:00:00")  # No tick=True or manual tick
def test_event_expiry_fail_without_tick():
    print("n--- Running test_event_expiry_fail_without_tick (EXPECT ASSERTION ERROR) ---")
    event_time = datetime.datetime.now()
    expiry_duration = 60
    print(f"  Event created at: {event_time}")

    # Simulate work or waiting - without tick, time doesn't advance!
    time.sleep(0.1)

    print(f"  Time after simulated wait: {datetime.datetime.now()}")
    print("  Checking expiry (incorrectly, time didn't move):")
    try:
        # This should ideally be True, but will be False without ticking
        assert check_if_event_expired(event_time, expiry_duration)
    except AssertionError:
        print("  AssertionError: Event did not expire, as expected without tick.")
    print("  Failure scenario finished.")

# Run both tests
test_event_expiry_manual_tick()
test_event_expiry_fail_without_tick()

This is the following issues.

Running test_event_expiry_manual_tick:
  Event created at: 2023-10-27 12:00:00
  Checking immediately after creation:
  Checking expiry: Now=2023-10-27 12:00:00, Event=2023-10-27 12:00:00, ExpiresAt=2023-10-27 12:01:00 -> Expired=False
  Ticking forward by 0:01:01...
  Time after ticking: 2023-10-27 12:01:01
  Checking after ticking:
  Checking expiry: Now=2023-10-27 12:01:01, Event=2023-10-27 12:00:00, ExpiresAt=2023-10-27 12:01:00 -> Expired=True
  Manual tick test finished.

--- Running test_event_expiry_fail_without_tick (EXPECT ASSERTION ERROR) ---
  Event created at: 2023-10-27 12:00:00
  Time after simulated wait: 2023-10-27 12:00:00
  Checking expiry (incorrectly, time didn't move):
  Checking expiry: Now=2023-10-27 12:00:00, Event=2023-10-27 12:00:00, ExpiresAt=2023-10-27 12:01:00 -> Expired=False
  AssertionError: Event did not expire, as expected without tick.
  Failure scenario finished.

Example 4: To check related dates

Freezegun confirms that “past time”.

import datetime
from freezegun import freeze_time

def format_relative_time(timestamp):
    now = datetime.datetime.now()
    delta = now - timestamp
    
    rel_time_str = ""
    if delta.days > 0:
        rel_time_str = f"{delta.days} days ago"
    elif delta.seconds >= 3600:
        hours = delta.seconds // 3600
        rel_time_str = f"{hours} hours ago"
    elif delta.seconds >= 60:
        minutes = delta.seconds // 60
        rel_time_str = f"{minutes} minutes ago"
    else:
        rel_time_str = "just now"
    print(f"  Formatting relative time: Now={now}, Timestamp={timestamp} -> '{rel_time_str}'")
    return rel_time_str

@freeze_time("2023-10-27 15:00:00")
def test_relative_time_formatting():
    print("nRunning test_relative_time_formatting:")
    
    # Event happened 2 days and 3 hours ago relative to frozen time
    past_event = datetime.datetime(2023, 10, 25, 12, 0, 0)
    assert format_relative_time(past_event) == "2 days ago"

    # Event happened 45 minutes ago
    recent_event = datetime.datetime.now() - datetime.timedelta(minutes=45)
    assert format_relative_time(recent_event) == "45 minutes ago"

    # Event happened just now
    current_event = datetime.datetime.now() - datetime.timedelta(seconds=10)
    assert format_relative_time(current_event) == "just now"
    
    print("  Relative time tests passed!")

test_relative_time_formatting()

# --- Failure Scenario ---
print("n--- Running relative time without freeze_time (EXPECT FAILURE) ---")
def test_relative_time_unfrozen():
    # Use the same past event timestamp
    past_event = datetime.datetime(2023, 10, 25, 12, 0, 0) 
    print(f"  Testing with past_event = {past_event}")
    # This will compare against the *actual* current time, not Oct 27th, 2023
    formatted_time = format_relative_time(past_event)
    try:
        assert formatted_time == "2 days ago" 
    except AssertionError:
        # The actual difference will be much larger!
        print(f"  AssertionError: Expected '2 days ago', but got '{formatted_time}'. Failed as expected.")

test_relative_time_unfrozen()

The result.

Running test_relative_time_formatting:
  Formatting relative time: Now=2023-10-27 15:00:00, Timestamp=2023-10-25 12:00:00 -> '2 days ago'
  Formatting relative time: Now=2023-10-27 15:00:00, Timestamp=2023-10-27 14:15:00 -> '45 minutes ago'
  Formatting relative time: Now=2023-10-27 15:00:00, Timestamp=2023-10-27 14:59:50 -> 'just now'
  Relative time tests passed!

--- Running relative time without freeze_time (EXPECT FAILURE) ---
  Testing with past_event = 2023-10-25 12:00:00
  Formatting relative time: Now=2023-10-27 12:00:00, Timestamp=2023-10-25 12:00:00 -> '2 days ago'

Example 5: Harvesting Days (Name End)

The charges against the test, such as jumping, reliably.

import datetime
from freezegun import freeze_time

def is_last_day_of_month(check_date):
    next_day = check_date + datetime.timedelta(days=1)
    is_last = next_day.month != check_date.month
    print(f"  Checking if {check_date} is last day of month: Next day={next_day}, IsLast={is_last}")
    return is_last

print("nRunning specific date logic tests:")

@freeze_time("2023-02-28") # Non-leap year
def test_end_of_february_non_leap():
    today = datetime.date.today()
    assert is_last_day_of_month(today) is True

@freeze_time("2024-02-28") # Leap year
def test_end_of_february_leap_not_yet():
     today = datetime.date.today()
     assert is_last_day_of_month(today) is False # Feb 29th exists

@freeze_time("2024-02-29") # Leap year - last day
def test_end_of_february_leap_actual():
    today = datetime.date.today()
    assert is_last_day_of_month(today) is True

@freeze_time("2023-12-31")
def test_end_of_year():
    today = datetime.date.today()
    assert is_last_day_of_month(today) is True

test_end_of_february_non_leap()
test_end_of_february_leap_not_yet()
test_end_of_february_leap_actual()
test_end_of_year()
print("Specific date logic tests passed!")



#
# Output
#


Running specific date logic tests:
Checking if 2023-02-28 is last day of month: Next day=2023-03-01, IsLast=True
Checking if 2024-02-28 is last day of month: Next day=2024-02-29, IsLast=False
Checking if 2024-02-29 is last day of month: Next day=2024-03-01, IsLast=True
Checking if 2023-12-31 is last day of month: Next day=2024-01-01, IsLast=True
pecific date logic tests passed!

Example 6: Time Areas

Timezone-hour-hour time code, managing offsets and changes such as BST / GMT.

# Requires Python 3.9+ for zoneinfo or `pip install pytz` for older versions
import datetime
from freezegun import freeze_time
try:
    from zoneinfo import ZoneInfo # Python 3.9+
except ImportError:
    from pytz import timezone as ZoneInfo # Fallback for older Python/pytz

def get_local_and_utc_time():
    # Assume local timezone is Europe/London for this example
    local_tz = ZoneInfo("Europe/London")
    now_utc = datetime.datetime.now(datetime.timezone.utc)
    now_local = now_utc.astimezone(local_tz)
    print(f"  Getting times: UTC={now_utc}, Local={now_local} ({now_local.tzname()})")
    return now_local, now_utc

# Freeze time as 9 AM UTC. London is UTC+1 in summer (BST). Oct 27 is BST.
@freeze_time("2023-10-27 09:00:00", tz_offset=0) # tz_offset=0 means the frozen time string IS UTC
def test_time_in_london_bst():
    print("nRunning test_time_in_london_bst:")
    local_time, utc_time = get_local_and_utc_time()
    assert utc_time.hour == 9
    assert local_time.hour == 10 # London is UTC+1 on this date
    assert local_time.tzname() == "BST" 

# Freeze time as 9 AM UTC. Use December 27th, which is GMT (UTC+0)
@freeze_time("2023-12-27 09:00:00", tz_offset=0)
def test_time_in_london_gmt():
    print("nRunning test_time_in_london_gmt:")
    local_time, utc_time = get_local_and_utc_time()
    assert utc_time.hour == 9
    assert local_time.hour == 9 # London is UTC+0 on this date
    assert local_time.tzname() == "GMT"

test_time_in_london_bst()
test_time_in_london_gmt()
print("nTimezone tests passed!")

#
# Output
#

 Running test_time_in_london_bst:
 Getting times: UTC=2023-10-27 09:00:00+00:00, Local=2023-10-27 10:00:00+01:00 (BST)

 Running test_time_in_london_gmt:
 Getting times: UTC=2023-12-27 09:00:00+00:00, Local=2023-12-27 09:00:00+00:00 (GMT)

 Timezone tests passed!

Example 7: Clear Time to Travel With Move_ato

Jump between points for some time in a single test of a temporary complex order.

import datetime
from freezegun import freeze_time

class ReportGenerator:
    def __init__(self):
        self.creation_time = datetime.datetime.now()
        self.data = {"status": "pending", "generated_at": None}
        print(f"  Report created at {self.creation_time}")

    def generate(self):
        self.data["status"] = "generated"
        self.data["generated_at"] = datetime.datetime.now()
        print(f"  Report generated at {self.data['generated_at']}")

    def get_status_update(self):
        now = datetime.datetime.now()
        if self.data["status"] == "generated":
            time_since_generation = now - self.data["generated_at"]
            status = f"Generated {time_since_generation.seconds} seconds ago."
        else:
            time_since_creation = now - self.creation_time
            status = f"Pending for {time_since_creation.seconds} seconds."
        print(f"  Status update at {now}: '{status}'")
        return status

def test_report_lifecycle():
    print("nRunning test_report_lifecycle:")
    with freeze_time("2023-11-01 10:00:00") as freezer:
        report = ReportGenerator()
        assert report.data["status"] == "pending"
        
        # Check status after 5 seconds
        target_time = datetime.datetime(2023, 11, 1, 10, 0, 5)
        print(f"  Moving time to {target_time}")
        freezer.move_to(target_time)
        assert report.get_status_update() == "Pending for 5 seconds."

        # Generate the report at 10:01:00
        target_time = datetime.datetime(2023, 11, 1, 10, 1, 0)
        print(f"  Moving time to {target_time} and generating report")
        freezer.move_to(target_time)
        report.generate()
        assert report.data["status"] == "generated"
        assert report.get_status_update() == "Generated 0 seconds ago."

        # Check status 30 seconds after generation
        target_time = datetime.datetime(2023, 11, 1, 10, 1, 30)
        print(f"  Moving time to {target_time}")
        freezer.move_to(target_time)
        assert report.get_status_update() == "Generated 30 seconds ago."
        
    print("  Complex lifecycle test passed!")

test_report_lifecycle()

# --- Failure Scenario ---
def test_report_lifecycle_fail_forgot_move():
    print("n--- Running lifecycle test (FAIL - forgot move_to) ---")
    with freeze_time("2023-11-01 10:00:00") as freezer:
        report = ReportGenerator()
        assert report.data["status"] == "pending"
        
        # We INTEND to check status after 5 seconds, but FORGET to move time
        print(f"  Checking status (time is still {datetime.datetime.now()})")
        # freezer.move_to("2023-11-01 10:00:05") # <-- Forgotten!
        try:
            assert report.get_status_update() == "Pending for 5 seconds."
        except AssertionError as e:
            print(f"  AssertionError: {e}. Failed as expected.")
            
test_report_lifecycle_fail_forgot_move()

Here is the result.

Running test_report_lifecycle:
  Report created at 2023-11-01 10:00:00
  Moving time to 2023-11-01 10:00:05
  Status update at 2023-11-01 10:00:05: 'Pending for 5 seconds.'
  Moving time to 2023-11-01 10:01:00 and generating report
  Report generated at 2023-11-01 10:01:00
  Status update at 2023-11-01 10:01:00: 'Generated 0 seconds ago.'
  Moving time to 2023-11-01 10:01:30
  Status update at 2023-11-01 10:01:30: 'Generated 30 seconds ago.'
  Complex lifecycle test passed!

--- Running lifecycle test (FAIL - forgot move_to) ---
  Report created at 2023-11-01 10:00:00
  Checking status (time is still 2023-11-01 10:00:00)
  Status update at 2023-11-01 10:00:00: 'Pending for 0 seconds.'
  AssertionError: . Failed as expected.

Summary

Freezegun is a good tool for any Python developer you need to check the dates and times. It changes the potential tests, which are trying to exercise and write them simple, strong, and details. By allowing you to free, tick, and go later – and by making it clear when time is not Controlled – opens the power to successfully perform the previous conditions.

Displaying this, I gave several examples that include different events involving the date and the testing time and shows how technology is completing obstacles framework for traditional examination.

While covering the basic performance, you can do more with freezegun, and I recommend to look at its GitTub page.

In short, the Freezen is a library to be able to know and use if your code works later and you need to put it well and reliably.

Source link

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button