Commit 8627b981 by David Miller

Add a -r --random option.

When passing -r or --random to the lettuce executable, we run both
features, and scenarios within features in a pseudo-random order.

This is helpful to detect issues that may arise from tests that
cause significant side-effects, the detection of locks etc
parent be50d4c0
...@@ -23,6 +23,7 @@ import sys ...@@ -23,6 +23,7 @@ import sys
import traceback import traceback
from imp import reload from imp import reload
from datetime import datetime from datetime import datetime
import random
from lettuce.core import Feature, TotalResult from lettuce.core import Feature, TotalResult
...@@ -76,7 +77,7 @@ class Runner(object): ...@@ -76,7 +77,7 @@ class Runner(object):
Takes a base path as parameter (string), so that it can look for Takes a base path as parameter (string), so that it can look for
features and step definitions on there. features and step definitions on there.
""" """
def __init__(self, base_path, scenarios=None, verbosity=0, def __init__(self, base_path, scenarios=None, verbosity=0, random=False,
enable_xunit=False, xunit_filename=None, tags=None): enable_xunit=False, xunit_filename=None, tags=None):
""" lettuce.Runner will try to find a terrain.py file and """ lettuce.Runner will try to find a terrain.py file and
import it from within `base_path` import it from within `base_path`
...@@ -107,6 +108,8 @@ class Runner(object): ...@@ -107,6 +108,8 @@ class Runner(object):
else: else:
from lettuce.plugins import colored_shell_output as output from lettuce.plugins import colored_shell_output as output
self.random = random
if enable_xunit: if enable_xunit:
xunit_output.enable(filename=xunit_filename) xunit_output.enable(filename=xunit_filename)
...@@ -132,6 +135,8 @@ class Runner(object): ...@@ -132,6 +135,8 @@ class Runner(object):
features_files = [self.single_feature] features_files = [self.single_feature]
else: else:
features_files = self.loader.find_feature_files() features_files = self.loader.find_feature_files()
if self.random:
random.shuffle(features_files)
if not features_files: if not features_files:
self.output.print_no_features_found(self.loader.base_dir) self.output.print_no_features_found(self.loader.base_dir)
...@@ -142,7 +147,7 @@ class Runner(object): ...@@ -142,7 +147,7 @@ class Runner(object):
for filename in features_files: for filename in features_files:
feature = Feature.from_file(filename) feature = Feature.from_file(filename)
results.append( results.append(
feature.run(self.scenarios, tags=self.tags)) feature.run(self.scenarios, tags=self.tags, random=self.random))
except exceptions.LettuceSyntaxError, e: except exceptions.LettuceSyntaxError, e:
sys.stderr.write(e.msg) sys.stderr.write(e.msg)
......
...@@ -47,6 +47,12 @@ def main(args=sys.argv[1:]): ...@@ -47,6 +47,12 @@ def main(args=sys.argv[1:]):
'(prefixing tags with "-" will exclude them and ' '(prefixing tags with "-" will exclude them and '
'prefixing with "~" will match approximate words)') 'prefixing with "~" will match approximate words)')
parser.add_option("-r", "--random",
dest="random",
action="store_true",
default=False,
help="Run scenarios in a more random order to avoid interference")
parser.add_option("--with-xunit", parser.add_option("--with-xunit",
dest="enable_xunit", dest="enable_xunit",
action="store_true", action="store_true",
...@@ -73,6 +79,7 @@ def main(args=sys.argv[1:]): ...@@ -73,6 +79,7 @@ def main(args=sys.argv[1:]):
base_path, base_path,
scenarios=options.scenarios, scenarios=options.scenarios,
verbosity=options.verbosity, verbosity=options.verbosity,
random=options.random,
enable_xunit=options.enable_xunit, enable_xunit=options.enable_xunit,
xunit_filename=options.xunit_file, xunit_filename=options.xunit_file,
tags=options.tags, tags=options.tags,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import random as rand
import re import re
import codecs import codecs
from fuzzywuzzy import fuzz from fuzzywuzzy import fuzz
...@@ -983,10 +984,13 @@ class Feature(object): ...@@ -983,10 +984,13 @@ class Feature(object):
return scenarios, description return scenarios, description
def run(self, scenarios=None, ignore_case=True, tags=None): def run(self, scenarios=None, ignore_case=True, tags=None, random=False):
call_hook('before_each', 'feature', self) call_hook('before_each', 'feature', self)
scenarios_ran = [] scenarios_ran = []
if random:
rand.shuffle(self.scenarios)
if isinstance(scenarios, (tuple, list)): if isinstance(scenarios, (tuple, list)):
if all(map(lambda x: isinstance(x, int), scenarios)): if all(map(lambda x: isinstance(x, int), scenarios)):
scenarios_to_run = scenarios scenarios_to_run = scenarios
......
...@@ -15,8 +15,9 @@ ...@@ -15,8 +15,9 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
import random
import lettuce import lettuce
from mock import Mock from mock import Mock, patch
from StringIO import StringIO from StringIO import StringIO
from os.path import dirname, join, abspath from os.path import dirname, join, abspath
from nose.tools import assert_equals, with_setup, assert_raises from nose.tools import assert_equals, with_setup, assert_raises
...@@ -1096,3 +1097,12 @@ def test_run_only_fast_tests(): ...@@ -1096,3 +1097,12 @@ def test_run_only_fast_tests():
"1 scenario (1 passed)\n" "1 scenario (1 passed)\n"
"2 steps (2 passed)\n" "2 steps (2 passed)\n"
) )
def test_run_random():
"Randomise the feature order"
filename = tag_feature_name('timebound')
runner = Runner('some_basepath', random=True)
assert_equals(True, runner.random)
with patch.object(random, 'shuffle') as pshuffle:
runner.run()
pshuffle.assert_called_once_with([])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment