classification
Title: TextTestResult uses TestCase.__str__() which isn't customisable (vs id() or shortDescription())
Type: enhancement Stage:
Components: Versions: Python 3.4
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: michael.foord Nosy List: chris.jerdonek, eric.araujo, michael.foord, r.david.murray, rbcollins
Priority: normal Keywords: easy

Created on 2012-10-19 18:34 by rbcollins, last changed 2012-10-21 22:03 by r.david.murray.

Messages (13)
msg173352 - (view) Author: Robert Collins (rbcollins) * Date: 2012-10-19 18:34
TextTestRunner calls str(TestCase) directly, which makes it hard for testscenarios to rename the test cases as it parameterises them (because __str__ is a descriptor). While testscenarios could use a decorator instead, thats undesirable as the test case object would still need to be customised so that calls to self.id() and self.shortDescription() inside it still return consistent information.

So the relevant code is this:
 def getDescription(self, test):
41 if self.descriptions:
42 return test.shortDescription() or str(test)
43 else:
44 return str(test)

What I'd like is to have this be something like:
41 if self.descriptions:
42     return test.shortDescription() or test.id()
43 else:
44     return test.id()

Which would let testscenarios adjust both shortDescriptions and id, and Just Work.
msg173354 - (view) Author: Robert Collins (rbcollins) * Date: 2012-10-19 18:47
Or anther way this could be done would be to make TestCase.__str__ call self.id(), and then __str__ never needs to be adjusted - id() or shortDescription are the only things to tweak.
msg173355 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-10-19 18:52
By "descriptor" I think you really mean it is a special method (ie: looked up on the class only, not on an instance).  A descriptor is something else.

This is a feature request and could only be implemented in 3.4.  Assuming I'm understanding what you are asking for (which is not certain), as a partial workaround you could reset _testMethodName.
msg173365 - (view) Author: Robert Collins (rbcollins) * Date: 2012-10-20 02:18
They aren't descriptors? They certainly aren't normal methods:
>>> class foo(object):
...   def __str__(self): return "foo"
... 
>>> f = foo()
>>> f.__str__ = lambda: "bar"
>>> str(f)
'foo'

I *thought* the mechanism by which they can only be replaced by altering the class *was* descriptors, but I gladly seek better info!

_testMethodName is overloaded though: its both 'what is the test method' and 'part of the description or id of the test', so resetting it would be - at best - risky when only changing the description or id.
msg173393 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-10-20 14:35
The special handling of special methods is baked into the attribute lookup machinery.  The discussion of this is in the language reference somewhere, as is the explanation of what descriptors are and how they work.
msg173394 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2012-10-20 15:09
> So the relevant code is this:
> def getDescription(self, test):
> ...
> 43 return str(test)
> ...
> What I'd like is to have this be something like:
> 44     return test.id()

> Or anther way this could be done would be to make TestCase.__str__ call self.id()

Note that str(test) and test.id() don't return the same value.  The former is the "friendly" name.  It is probably intentional that TextTestResult uses the TestCase instance's friendly name in its getDescription() method as opposed to the id.

> TextTestRunner calls str(TestCase) directly, which makes it hard for testscenarios to rename the test cases as it parameterises them

What about testscenarios subclassing the TestCase instances as it parametrizes them?

    class ScenariosTestCase(case.__class__):
        def __str__(self):
            return "testscenarios test name..."
    case.__class__ = ScenariosTestCase
    print(str(case))

From the documentation, it looks like testscenarios creates new test objects from the originals anyways.  Alternatively (although I don't if testscenarios can control what test runner is being used), TextTestRunner could be subclassed with its own getDescription() method.
msg173395 - (view) Author: √Čric Araujo (eric.araujo) * (Python committer) Date: 2012-10-20 15:09
http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes
msg173467 - (view) Author: Robert Collins (rbcollins) * Date: 2012-10-21 18:24
testscenarios copies the tests, it doesn't call the constructor for the class; this makes things a lot simpler than trying to reconstruct whatever state the object may have from scratch again.

As for str(test) and test.id() being different - well sure they are today, but I don't know that the str(test) format is /useful/ today, as its not a particularly good str() anyhow. It doesn't identify that its a test instance even. 

This suggests two alternatives to me:
 - decide that we want three things: id, friendly-id and shortDescription, and have three non-magic methods, which TextTestRunner can call depending on what the user wants to see.
 - decide we want two things, id and shortDescription, and TextTestRunner can combine these things to meet the users request. (e.g. id + ' ' + shortDescription)

And separately, as the __str__ isn't particularly good anyhow, perhaps we should take the opportunity to think about what we want from it and adjust it.
msg173468 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2012-10-21 18:40
So three including str sounds sufficient to me: short description, long description and repr (with str == repr) for debugging.
msg173472 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-10-21 19:24
Robert: I don't know if there's something funky going on with your browser, but every time you post the 'enhancement' setting for type seems to get lost.
msg173473 - (view) Author: Robert Collins (rbcollins) * Date: 2012-10-21 19:49
@Michael I'll put a patch together next week then.
@R.david.murray no idea - but I've refreshed the page, we'll if it behaves better. My guess would be a buggy in-flight-collision detection in the issue tracker code.
msg173474 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2012-10-21 19:49
> testscenarios copies the tests, it doesn't call the constructor for the class;

My suggestion on how to override __str__ (by assignment to __class__) doesn't require that the constructor be called.
msg173490 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-10-21 22:03
Much more likely that you just needed to refresh the page, going by my own experience with this kind of problem (especially seeing as that seems to have fixed it :)
History
Date User Action Args
2012-10-21 22:03:34r.david.murraysetmessages: + msg173490
2012-10-21 19:49:45chris.jerdoneksetmessages: + msg173474
2012-10-21 19:49:34rbcollinssetmessages: + msg173473
2012-10-21 19:24:48r.david.murraysettype: enhancement
messages: + msg173472
2012-10-21 18:40:50michael.foordsetmessages: + msg173468
2012-10-21 18:27:24mikehoysetnosy: - mikehoy
2012-10-21 18:24:27rbcollinssettype: enhancement -> (no value)
messages: + msg173467
2012-10-21 17:40:36michael.foordsetassignee: michael.foord
2012-10-20 15:09:31eric.araujosetnosy: + eric.araujo
messages: + msg173395
2012-10-20 15:09:28chris.jerdoneksetmessages: + msg173394
2012-10-20 14:35:40r.david.murraysettype: enhancement
messages: + msg173393
2012-10-20 13:53:33mikehoysetnosy: + mikehoy
2012-10-20 02:18:39rbcollinssettype: enhancement -> (no value)
messages: + msg173365
2012-10-19 22:05:26chris.jerdoneksetnosy: + chris.jerdonek
2012-10-19 18:52:43r.david.murraysetversions: - Python 3.1, Python 2.7, Python 3.2, Python 3.3, Python 3.5
nosy: + r.david.murray

messages: + msg173355

keywords: + easy
type: enhancement
2012-10-19 18:47:22rbcollinssetmessages: + msg173354
2012-10-19 18:34:49rbcollinscreate