Code tests around time in Python

1.First of all

If the time test is performed manually, it can be annoying to change the sysdate, and bugs are likely to be mixed in. So I coded the test, ran it more easily, and tried to improve the quality.

2. Execution environment, tools

The execution environment is as follows

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.1
BuildVersion:	19B88

$ python --version
Python 3.7.4

Also, for a quick test around the time, try using a package called freezegun. You can use this package called freezegun to replace the current time obtained from datetime in the standard library of Python with the specified one.

3. Use freezegun

When testing the processing around the time, of course, I want to test using various times. However, running the test while changing the system time just for that is annoying. So if you use freezegun, you won't have to do that. Install the package from the pip command.

$ pip install freezegun

4. Try to return a specific time

In freezegun,freezegun.freeze_time ()can be replaced so that the function of the datetime module returns a specific time. The sample code looks at the contents of datetime.now ().

app_freezegun.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import freezegun
from datetime import datetime


def main():
    #Replace and display current time using freezegun
    freezer = freezegun.freeze_time('2015-10-21')
    freezer.start()
    try:
        print("freezegun:" + str(datetime.now()))
    finally:
        freezer.stop()
    
    #Show current time
    print("nowtime:" + str(datetime.now()))


if __name__ == '__main__':
    main()

Do the above.

$ python app_freezegun.py
freezegun:2015-10-21 00:00:00
nowtime:2019-12-14 10:16:49.847317

You can see that the current time has been replaced while using the freezegun.

5. Use as a decorator

Next is a pattern that uses the API as a decorator. The time is replaced only within the decorator-qualified function.

app_freezegun.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import freezegun
from datetime import datetime

#Replace and display current time using freezegun
@freezegun.freeze_time('2015-10-21')
def main():
    print("freezegun:" + str(datetime.now()))

#Show current time
def main_2():
    print("nowtime:" + str(datetime.now()))


if __name__ == '__main__':
    main()
    main_2()

Do the above.

$ python app_freezegun.py
freezegun:2015-10-21 00:00:00
nowtime:2019-12-14 10:16:49.847317

You can see that the current time in the decorator-qualified function has been replaced. Of course, you can also specify the time.

app_freezegun.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import freezegun
from datetime import datetime

#Replace current time using freezegun
@freezegun.freeze_time('2015-10-21 12:34:56')
def main():
    print("freezegun:" + str(datetime.now()))


if __name__ == '__main__':
    main()

The result is as follows.

$ python app_freezegun.py 
freezegun:2015-10-21 12:34:56

6. Use as a context manager

Earlier I specified the time using a decorator. The same API can also be used as a context manager. The following is the pattern used as the context manager. In this case, the time is replaced only within the context manager block.

app_freezegun.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import freezegun
from datetime import datetime


def main():
    #Replace and display current time using freezegun
    with freezegun.freeze_time('2015-10-21'):
        print("freezegun:" + str(datetime.now()))

    #Show current time
    print("nowtime:" + str(datetime.now()))


if __name__ == '__main__':
    main()

The result is the same as [4.], so it is omitted.

7. Shift the time from a specific time

When conducting a test, I think there is a need to once set the time as a reference time and then shift the time after performing specific processing. Try using freezegun.

In the following sample code, tick () is used to shift the time by the timedelta object, andmove_to ()is used to switch the time.

app_freezegun.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import freezegun
from datetime import datetime
from datetime import timedelta


def main():
    with freezegun.freeze_time('2015-10-21 00:00:00') as freeze_datetime:
        print(datetime.now())

        #Advance time by 1 second
        freeze_datetime.tick()
        print(datetime.now())

        #Advance the time by 1 minute
        freeze_datetime.tick(delta=timedelta(minutes=1))
        print(datetime.now())

        #Move to a specific time
        freeze_datetime.move_to('2019-01-01 00:00:00')
        print(datetime.now())


if __name__ == '__main__':
    main()

Do the above.

$ python app_freezegun.py 
2015-10-21 00:00:00
2015-10-21 00:00:01
2015-10-21 00:01:01
2019-01-01 00:00:00

8. Try incorporating it into a unit test

I verified various things, but here is the actual production. Use freezegun in ʻunittest. I want to test what is created automatically using datetime`, so mock AWS EC2. First, prepare the app. Get the created EC2, get the one older than the execution date and time of the application, and return the instance name.

app_freezegun.py



#!/usr/bin/env python
# -*- coding: utf-8 -*-
import boto3
from datetime import datetime, timedelta, timezone


def main():
    #Get the current time
    now = datetime.now(timezone.utc)
    #Initialize instance name as an array
    instace_names = []

    client = boto3.client('ec2')

    #Get all instance information.
    instances = client.describe_instances()

    #Get the instance created before the execution date.
    for instance_list in instances.get('Reservations'):
        for instance in instance_list.get('Instances'):
            if now > instance.get('LaunchTime'):

                #Extract the instance name.
                instace_names.append(instance.get('KeyName'))

    return instace_names

Next is the test code. Use freezegun to mock 4 EC2s with different creation dates.

test_freezegun.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import boto3
from moto import mock_ec2
import freezegun
from datetime import datetime, timedelta
from app import app_freezegun as app


class MyTestCase(unittest.TestCase):
    @mock_ec2
    def test_case_1(self):
        with freezegun.freeze_time('2017-01-01 00:00:00') as freeze_datetime:
            #Created date 2017-01-Create EC2 with 01
            self.__common('test_ec2_name_1')

            freeze_datetime.move_to('2018-01-01 00:00:00')
            #Creation date 2018-01-Create EC2 with 01
            self.__common('test_ec2_name_2')

            freeze_datetime.move_to('2019-01-01 00:00:00')
            #Creation date 2019-01-Create EC2 with 01
            self.__common('test_ec2_name_3')

            freeze_datetime.move_to('2020-01-01 00:00:00')
            #Creation date 2020-01-Create EC2 with 01
            self.__common('test_ec2_name_4')

        #Run the app
        instance_names = app.main()

        #Check the result
        self.assertEqual(instance_names, ['test_ec2_name_1', 'test_ec2_name_2', 'test_ec2_name_3'])

    def __common(self, name):
        client = boto3.client('ec2')
        #Set the conditions for EC2 to be created
        ec2objects = [
            {'KeyName': name}
        ]
        #Create EC2
        for o in ec2objects:
            client.run_instances(
                ImageId='ami-03cf127a',
                MinCount=1,
                MaxCount=1,
                KeyName=o.get('KeyName'))


if __name__ == '__main__':
    unittest.main()

Try to run it.

$ python -m unittest tests.test_freezegun -v
test_case_1 (tests.test_freezegun.MyTestCase) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.387s

OK

I did well.

Recommended Posts

Code tests around time in Python
Generate QR code in Python
Character code learned in Python
[Python] Generate QR code in memory
Automatically format Python code in Vim
Measure function execution time in Python
Write selenium test code in python
Write C unit tests in Python
Fourier series verification code written in Python
Just print Python elapsed time in seconds
Get the EDINET code list in Python
Notes on using code formatter in Python
Time comparison: Correlation coefficient calculation in Python
Quadtree in Python --2
Python in optimization
CURL in python
Metaprogramming in Python
First time python
Python 3.3 in Anaconda
Geocoding in python
SendKeys in Python
Meta-analysis in Python
Unittest in python
Epoch in Python
python time measurement
Sudoku in Python
DCI in Python
python character code
quicksort in python
nCr in python
N-Gram in Python
Programming in python
First time python
Plink in Python
Constant in python
Lifegame in Python.
FizzBuzz in Python
[Python] Algorithm-aware code
Sqlite in python
StepAIC in Python
N-gram in python
LINE-Bot [0] in Python
Csv in python
Disassemble in Python
Reflection in Python
Constant in python
nCr in Python.
format in python
PPAP in Python
Quad-tree in Python
Python unit tests
Reflection in Python
Chemistry in Python
Hashable in python
DirectLiNGAM in Python
LiNGAM in Python
Flatten in python
flatten in python
Get time series data from k-db.com in Python
Time when terminal disappears in Visual Studio Code
Personal notes to doc Python code in Sphinx