diff options
author | John Szakmeister <john@szakmeister.net> | 2015-11-28 04:10:21 -0500 |
---|---|---|
committer | John Szakmeister <john@szakmeister.net> | 2015-11-28 04:10:21 -0500 |
commit | ad63d9a357d07c74b4cdcc4da32c7a02e03a57a6 (patch) | |
tree | d56f7fc183b31ed6657688d4e0e3e2691d2d1a39 | |
parent | 7c8f005616223d4f585204535c7c5c800daaf0fb (diff) | |
parent | c37ce12ae1d73c4ba9bcf933a08a6f1d12b081a0 (diff) | |
download | nose-ad63d9a357d07c74b4cdcc4da32c7a02e03a57a6.tar.gz |
Merge pull request #951 from LewisHaley/fix-test-repr-with-mutable-args
Fix test repr with mutable args
-rw-r--r-- | nose/case.py | 50 | ||||
-rw-r--r-- | unit_tests/test_cases.py | 40 |
2 files changed, 61 insertions, 29 deletions
diff --git a/nose/case.py b/nose/case.py index cffa4ab..c585c2b 100644 --- a/nose/case.py +++ b/nose/case.py @@ -199,7 +199,7 @@ class TestBase(unittest.TestCase): def shortDescription(self): if hasattr(self.test, 'description'): return self.test.description - func, arg = self._descriptors() + func = self._descriptor() doc = getattr(func, '__doc__', None) if not doc: doc = str(self) @@ -239,6 +239,7 @@ class FunctionTestCase(TestBase): self.setUpFunc = setUp self.tearDownFunc = tearDown self.arg = arg + self.arg_repr = repr(self.arg) self.descriptor = descriptor TestBase.__init__(self) @@ -276,32 +277,27 @@ class FunctionTestCase(TestBase): try_run(self.test, names) def __str__(self): - func, arg = self._descriptors() + func = self._descriptor() if hasattr(func, 'compat_func_name'): name = func.compat_func_name else: name = func.__name__ name = "%s.%s" % (func.__module__, name) - if arg: - name = "%s%s" % (name, arg) + if not self.arg_repr == '()': + name = "%s%s" % (name, self.arg_repr) # FIXME need to include the full dir path to disambiguate # in cases where test module of the same name was seen in # another directory (old fromDirectory) return name __repr__ = __str__ - def _descriptors(self): - """Get the descriptors of the test function: the function and - arguments that will be used to construct the test name. In - most cases, this is the function itself and no arguments. For - tests generated by generator functions, the original - (generator) function and args passed to the generated function - are returned. + def _descriptor(self): + """Get the descriptor of the test method. + + If a `descriptor` was specified for __init__, then it is returned, else + the test method (callable object) is returned. """ - if self.descriptor: - return self.descriptor, self.arg - else: - return self.test, self.arg + return self.descriptor if self.descriptor is not None else self.test class MethodTestCase(TestBase): @@ -338,6 +334,7 @@ class MethodTestCase(TestBase): self.method = method self.test = test self.arg = arg + self.arg_repr = repr(self.arg) self.descriptor = descriptor if isfunction(method): raise ValueError("Unbound methods must be wrapped using pyversion.unbound_method before passing to MethodTestCase") @@ -349,7 +346,7 @@ class MethodTestCase(TestBase): TestBase.__init__(self) def __str__(self): - func, arg = self._descriptors() + func = self._descriptor() if hasattr(func, 'compat_func_name'): name = func.compat_func_name else: @@ -357,8 +354,8 @@ class MethodTestCase(TestBase): name = "%s.%s.%s" % (self.cls.__module__, self.cls.__name__, name) - if arg: - name = "%s%s" % (name, arg) + if not self.arg_repr == '()': + name = "%s%s" % (name, self.arg_repr) return name __repr__ = __str__ @@ -383,15 +380,10 @@ class MethodTestCase(TestBase): def tearDown(self): try_run(self.inst, ('teardown', 'tearDown')) - def _descriptors(self): - """Get the descriptors of the test method: the method and - arguments that will be used to construct the test name. In - most cases, this is the method itself and no arguments. For - tests generated by generator methods, the original - (generator) method and args passed to the generated method - or function are returned. + def _descriptor(self): + """Get the descriptor of the test method. + + If a `descriptor` was specified for __init__, then it is returned, else + the test method (callable object) is returned. """ - if self.descriptor: - return self.descriptor, self.arg - else: - return self.method, self.arg + return self.descriptor if self.descriptor is not None else self.method diff --git a/unit_tests/test_cases.py b/unit_tests/test_cases.py index af399e5..fd5302d 100644 --- a/unit_tests/test_cases.py +++ b/unit_tests/test_cases.py @@ -115,6 +115,46 @@ class TestNoseCases(unittest.TestCase): f(res) assert res.errors + def test_FunctionTestCase_repr_is_consistent_with_mutable_args(self): + class Foo(object): + def __init__(self): + self.bar = 'unmodified' + def __repr__(self): + return "Foo(%s)" % self.bar + + def test_foo(foo): + pass + + foo = Foo() + case = nose.case.FunctionTestCase(test_foo, arg=(foo,)) + case_repr_before = case.__repr__() + foo.bar = "snafu'd!" + case_repr_after = case.__repr__() + assert case_repr_before == case_repr_after, ( + "Modifying a mutable object arg during test case changed the test " + "case's __repr__") + + def test_MethodTestCase_repr_is_consistent_with_mutable_args(self): + class Foo(object): + def __init__(self): + self.bar = 'unmodified' + def __repr__(self): + return "Foo(%s)" % self.bar + + class FooTester(object): + def test_foo(self, foo): + pass + + foo = Foo() + case = nose.case.FunctionTestCase( + unbound_method(FooTester, FooTester.test_foo), arg=(foo,)) + case_repr_before = case.__repr__() + foo.bar = "snafu'd!" + case_repr_after = case.__repr__() + assert case_repr_before == case_repr_after, ( + "Modifying a mutable object arg during test case changed the test " + "case's __repr__") + class TestNoseTestWrapper(unittest.TestCase): def test_case_fixtures_called(self): |