Skip to content

Commit 8cf8b3c

Browse files
author
Harsh Murari
committed
Initial commit for python test runner gui
1 parent d2457ed commit 8cf8b3c

11 files changed

+2329
-1
lines changed

README.md

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,56 @@
11
# pytest-gui
2-
GUI for running Python Unit Tests
2+
3+
This provides a GUI based test runner for Python Unittests and py.tests.
4+
5+
* Load Test cases from any directory.
6+
* Run single test, multiple selected tests or all tests.
7+
* Auto-Discover test cases recursively; in any subdirectory under current directory.
8+
* Display status of every test run, including test output, description, duration and any errors.
9+
* Status text at the bottom showing how many test cases are run, passed, failed or skipped.
10+
* Status bar shown while tests are running - showing progress bar on test completion.
11+
12+
## Getting Started
13+
14+
* Clone the pytest-gui repository
15+
* Install dependencies (`pip install -r requirements.txt`)
16+
* Run command and select the directory to search/run tests from (`python main.py`)
17+
18+
## General Usage
19+
20+
Initial screen consists of a left side pane, showing all the test cases that are discovered
21+
in the selected directory. By default, this would load tests from **_../tests_** directory. We can
22+
load tests from any other directory using the _Reload Tests_ button.
23+
24+
On left side we can select one or multiple test cases. To select multiple test cases, click the control
25+
button and select more test cases. If a parent of a test case is selected, then all tests under that
26+
parent are selected to run.
27+
28+
## Commands.
29+
30+
### Run Button
31+
32+
The Run button provides option to run selected test cases. This would run all test cases that are selected.
33+
If any higher level tests are selected (either a test class or a test file), then all tests under that test
34+
are executed.
35+
36+
### Re-run Button
37+
38+
The Re-run button runs only failures. If no tests have been run, this is similar to Run-All button whereby
39+
it would run all test cases.
40+
41+
### Run-all Button
42+
43+
This would run all test cases.
44+
45+
### Stop Button
46+
47+
This would stop any running test cases. The status of already run test cases would remain as is.
48+
49+
## Test Case Status
50+
51+
There are 4 test cases statuses and they are appropriately color-coded.
52+
53+
* **_Unrun_**: Test cases are not run yet. They would be highlighted with black color.
54+
* **_Pass_**: Test cases are run and passing. They would be highlighted with green color. We also show a green circle on the right pane in details section.
55+
* **_Fail_**:Test cases are run and failing. They would be highlighted with red color. We also show a red circle on the right pane in details section.
56+
* **_Skip_**: Test cases are skipped using @unittest.skip directive. They would be highlighted with blue color. We also see a blue circle on the right pane in details section.

__init__.py

Whitespace-only changes.

compat.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import sys
2+
import os
3+
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
4+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
5+
6+

discover.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# This file prints discovers all test cases and prints out their IDs.
2+
import unittest
3+
import argparse
4+
5+
class Discover:
6+
def __init__(self):
7+
self.tests = []
8+
9+
def flatten_results(self, iterable):
10+
input = list(iterable)
11+
while input:
12+
item = input.pop(0)
13+
try:
14+
data = iter(item)
15+
input = list(data) + input
16+
except:
17+
yield item
18+
19+
def collect_tests(self, dirname):
20+
loader = unittest.TestLoader()
21+
suite = loader.discover(dirname)
22+
flatresults = list(self.flatten_results(suite))
23+
self.tests = [r.id() for r in flatresults]
24+
25+
def print_tests(self):
26+
print('\n'.join(self.tests).strip())
27+
28+
if __name__ == '__main__':
29+
parser = argparse.ArgumentParser()
30+
parser.add_argument('--testdir', dest='testdir', default='../tests/legacy', help='Directory to choose tests from')
31+
options = parser.parse_args()
32+
33+
disc = Discover()
34+
disc.collect_tests(options.testdir)
35+
disc.print_tests()

events.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generic UI events structure
2+
3+
class EventSource(object):
4+
"""Generate GUI events.
5+
"""
6+
_events = {}
7+
8+
@classmethod
9+
def bind(cls, event, handler):
10+
cls._events.setdefault(cls, {}).setdefault(event, []).append(handler)
11+
12+
def emit(self, event, **data):
13+
try:
14+
for handler in self._events[self.__class__][event]:
15+
handler(self, **data)
16+
except KeyError:
17+
# No handler registered for event.
18+
pass

main.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'''
2+
Module for creating a project and running main Loop
3+
'''
4+
5+
try:
6+
from Tkinter import *
7+
except ImportError:
8+
from tkinter import *
9+
10+
from guitest.view import MainWindow
11+
from guitest.model import UnittestProject
12+
13+
14+
def main_loop(Model=UnittestProject):
15+
'''Run the main loop of the app.
16+
'''
17+
# Set up the root Tk context
18+
root = Tk()
19+
20+
# Construct an empty window
21+
view = MainWindow(root)
22+
23+
# Load the project model
24+
view.project = view.load_project(root, Model)
25+
26+
# Run the main loop
27+
view.mainloop()
28+
29+
if __name__ == "__main__":
30+
main_loop()

0 commit comments

Comments
 (0)