CleanCut / green

Green is a clean, colorful, fast python test runner.
MIT License
791 stars 75 forks source link

does green support doctest? #88

Closed SamuraiT closed 4 years ago

SamuraiT commented 8 years ago

Does green support doctest? I am using doctest, but seems like green does not support doctest.

CleanCut commented 8 years ago

Though I am familiar with the concept of doctests, I have never personally used them or even looked into them. So no, green does not currently support them unless our close ties to the built-in unittest framework brings it in for free.

Tell me, how do you usually write/run doctests? Is it part of unittest? Is it part of python? Is it a 3rd-party add-on? I'm curious.

SamuraiT commented 8 years ago

all right. I am using just built-in doctest. let's say I have a file called foo.py which is

# foo.py
class Foo(object):

    def bar(sel):
        """
        >>> foo = Foo()
        >>> foo.bar()
        'foobar'
        """
        return "foobar"

and just run the test

$ python3 -m doctest -v foo.py

foo = Foo()
Expecting nothing
ok
Trying:
    foo.bar()
Expecting:
    'foobar'
ok
2 items had no tests:
    foo
    foo.Foo
1 items passed all tests:
   2 tests in foo.Foo.bar
2 tests in 3 items.
2 passed and 0 failed.
Test passed.

just like that, but as you can see, it's hard to understand the stdard output. I wish you could solve it or I could help solving it

CleanCut commented 8 years ago

I think it would be great if you gave it a try! That would be a wonderful feature to add to Green. I wonder if doctest uses unittest under the hood. If it does, that would be a great place to do the integration.

SamuraiT commented 8 years ago

I never checked out that doctest runs under the unittest, I'll check it out.

aperezdc commented 8 years ago

@CleanCut @SamuraiT: It is possible to run doctests with unittest, by wrapping the doctests into a DocTestSuite or a DocFileSuite like in this example. AFAIK these classes provided by the doctest module have the same API as the unittest.TestSuite class, and that's how unittest is able to run them. Also, note how the linked example has a load_tests() function (related: #87) to let unittest know about the additional tests.

charles-l commented 4 years ago

@aperezdc

I've built a test program to do this:

# test_doctest.py                                                                                                                                                                                                                                                                                                           
import doctest                                                                                                                                                                                                                                                                                                                 

def f():                                                                                                                                                                                                                                                                                                                       
    '''                                                                                                                                                                                                                                                                                                                        
    >>> f()                                                                                                                                                                                                                                                                                                                    
    2                                                                                                                                                                                                                                                                                                                          
    '''                                                                                                                                                                                                                                                                                                                        
    return 2                                                                                                                                                                                                                                                                                                                   

def load_tests(loader, tests, ignore):                                                                                                                                                                                                                                                                                         
    return doctest.DocTestSuite('test_doctest')

With unittest, it will run:

python3 -m unittest test_doctest.py

green currently hangs, but I submitted PR #214 to address that. When I run it now, I get the following error:

  File "/home/nc/Dev/green/green/process.py", line 335, in poolRunner                                                                                                                                                                                                                                                          
    result.startTest(test)                                                                                                                                                                                                                                                                                                     
  File "/home/nc/Dev/green/green/result.py", line 237, in startTest                                                                                                                                                                                                                                                            
    test = proto_test(test)                                                                                                                                                                                                                                                                                                    
  File "/home/nc/Dev/green/green/result.py", line 31, in proto_test                                                                                                                                                                                                                                                            
    return ProtoTest(test)                                                                                                                                                                                                                                                                                                     
  File "/home/nc/Dev/green/green/result.py", line 68, in __init__                                                                                                                                                                                                                                                              
    self.method_name = str(test).split()[0]                                                                                                                                                                                                                                                                                    
  File "/usr/lib/python3.8/unittest/suite.py", line 27, in __repr__                                                                                                                                                                                                                                                            
    return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))                                                                                                                                                                                                                                                       
  File "/usr/lib/python3.8/doctest.py", line 2401, in __repr__                                                                                                                                                                                                                                                                 
    return self._dt_test.filename                                                                                                                                                                                                                                                                                              
AttributeError: 'str' object has no attribute 'filename'                                                                                                                                                                                                                                                                       

So it looks like at some part inside green the DocTestCase._dt_test field is getting turned into a string...

CleanCut commented 4 years ago

Support for doctests was added in version 3.1.0, just released. It requires choosing which modules you want to execute doctests from (for example, lets say you have doctests in myproj.foo and myproj.bar), and then in one of your test modules that contains unit tests adding a doctest_modules = [ ... ] list containing the modules you want to import the doctests from. For example, if you had a test module:

# test_stuff.py
import unittest

class TestSomething(unittest.TestCase)
    def test_something(self):
        # You need to have at least one unittest TestCase subclass with a test method
        pass

# Then this will work
import myproj.foo
doctest_modules = [
    myproj.foo,  # An actual module is best, because then we don't have to "discover" it via the filesystem
    "myproj.bar"  # But you can indicate the module with a string if you must
]