Quantcast
Channel: Planet Python
Viewing all articles
Browse latest Browse all 22465

Mike Driscoll: How to Run Python Tests “Continuously” While Coding

$
0
0

Last week I was doing some Test Driven Development training and overheard someone mention another programming language that had a test runner that you could set up to watch your project directory and run your tests when the files changed. I thought that was a neat idea. I also thought I could easily write my own Python script to do the same thing. Here’s a pretty rough version:

import argparse
importosimportsubprocessimporttime 
 
def get_args():
    parser = argparse.ArgumentParser(
        description="A File Watcher that executes the specified tests")parser.add_argument('--tests', action='store', required=True,
                        help='The path to the test file to run')parser.add_argument('--project', action='store', required=False,
                        help='The folder where the project files are')returnparser.parse_args() 
 
def watcher(test_path, project_path=None):
    ifnot project_path:
        project_path = os.path.dirname(test_path) 
    f_dict = {} 
    whileTrue:
        files = os.listdir(project_path)for f in files:
            full_path = os.path.join(project_path, f)
            mod_time = os.stat(full_path).st_mtimeif full_path notin f_dict:
                f_dict[full_path] = mod_time
            elif mod_time != f_dict[full_path]:
                # Run the testscmd = ['python', test_path]subprocess.call(cmd)print('-'*70)
                f_dict[full_path] = mod_time
 
        time.sleep(1) 
 
def main():
    args = get_args()
    w = watcher(args.tests, args.project) 
if __name__ == '__main__':
    main()

To run this script, you would need to do something like this:


python watcher.py --test ~/path/to/tests.py --project ~/project/path

Now let’s take a moment to talk about this script. The first function uses Python’s argparse module to make the program accept up to two command line arguments: –test and –project. The first is the path to the Python test script while the second is for the folder where the code that is to be tested resides. The next function, watcher, will loop forever and grab all the files out of the folder that was passed in or use the folder that the test file is in. It will grab each file’s modified time and save it to a dictionary. The key is set to the full path of the file and the value is the modification time. Next we check if the modification time has changed. If not, we sleep for a second and check again. If it has changed, then we run the tests.

At this point, you should be able to edit your code and tests in your favorite Python editor and watch your tests run in the terminal.


Using Watchdog

I looked around for other cross-platform methods of watching a directory and came across the watchdog project. It hasn’t been updated since 2015 (at the time of writing), but I tested it out and it seemed to work fine for me. You can install watchdog using pip:


pip install watchdog

Now that we have watchdog installed, let’s create some code that does something similar to the previous example:

import argparse
importosimportsubprocessimporttime 
from watchdog.observersimport Observer
from watchdog.eventsimport FileSystemEventHandler
 
def get_args():
    parser = argparse.ArgumentParser(
        description="A File Watcher that executes the specified tests")parser.add_argument('--tests', action="store", required=True,
                        help='The path to the test file to run')parser.add_argument('--project', action='store', required=False,
                        help='The folder where the project files are')returnparser.parse_args() 
 
class FW(FileSystemEventHandler):
    def__init__(self, test_file_path):
        self.test_file_path = test_file_path
 
    def on_any_event(self, event):
 
        ifos.path.exists(self.test_file_path):
            cmd = ['python', self.test_file_path]subprocess.call(cmd)print('-'*70) 
if __name__ =='__main__':
    args = get_args()
    observer = Observer()
    path = args.tests
    watcher = FW(path) 
    ifnot args.project:
        project_path = os.path.dirname(args.tests)else:
        project_path = args.project 
    ifos.path.exists(path)andos.path.isfile(path):
        observer.schedule(watcher, project_path, recursive=True)
        observer.start()try:
            whileTrue:
                time.sleep(1)exceptKeyboardInterrupt:
            observer.stop()
        observer.join()else:
        print('There is something wrong with your test path')

In this code, we keep our get_args() function and add a class. The class subclass’s watchdog’s FileSystemEventHandler class. We end up passing in our test file path to the class and override the on_any_event() method. This method fires on any time of file system event. When that happens, we run our tests. The last bit is at the end of the code where we create an Observer() object and tell it to watch the specified project path and to call our event handler should anything happen to the files there.


Wrapping Up

At this point, you should be able to start trying out these ideas on your own code. There are also some platform specific methods to watch a folder as well (like PyWin32) but if you run on multiple operating systems like I do, then watchdog or rolling your own might be a better choice.

Related Readings


Viewing all articles
Browse latest Browse all 22465

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>