Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testbook fails when using pytest test-classes #132

Open
bytinbit opened this issue Jul 28, 2021 · 2 comments
Open

Testbook fails when using pytest test-classes #132

bytinbit opened this issue Jul 28, 2021 · 2 comments

Comments

@bytinbit
Copy link

If I want to group my tests using regular pytest test classes, testbook is unable to execute and fails. It might be because the self argument of the test class method is erroneously filled in by the fixture.

Code cell to be tested (just for the record):

def reverse(items):
    reversed = []
    for item in items:
        reversed.insert(0, item)
    return reversed

fruits = ['apple', 'pear', 'cherry', 'lemon', 'mango']
print(reverse(fruits))

Test code that reproduces the error:

from pathlib import Path
from testbook import testbook

path = Path("lists.ipynb")

class TestReversingLists:
    @testbook(path, execute="reversing_lists")  # tag set in notebook
    def test_reversing_lists_output(self, tb):  # note the self
        reverse = tb.get("reverse")
        result = reverse(["alpha", "beta", "gamma", "delta", "epsilon", "zeta"])
        assert result == ["zeta", "epsilon", "delta", "gamma", "beta", "alpha"]

Error output when executing pytest + the file that contains the above code:

======================================================= FAILURES ========================================================
____________________________________ TestReversingLists.test_reversing_lists_output _____________________________________

self = <testbook.client.TestbookNotebookClient object at 0x7f787991b5b0>
tb = <test_reversing_lists.TestReversingLists object at 0x7f787748b0d0>

    @testbook(path, execute="reversing_lists")
    def test_reversing_lists_output(self, tb):
>       reverse = tb.get("reverse")
E       AttributeError: 'TestReversingLists' object has no attribute 'get'

04-lists/tests/test_reversing_lists.py:9: AttributeError
================================================ short test summary info ================================================
FAILED 04-lists/tests/test_reversing_lists.py::TestReversingLists::test_reversing_lists_output - AttributeError: 'Test...
=================================================== 1 failed in 1.29s ===================================================

Without a test class, the code works perfectly fine when executing pytest + the file that contains the above code:

from pathlib import Path
from testbook import testbook

path = Path("lists.ipynb")

@testbook(path, execute="reversing_lists")  # tag set in notebook
def test_reversing_lists_output(tb):
    reverse = tb.get("reverse")
    result = reverse(["alpha", "beta", "gamma", "delta", "epsilon", "zeta"])
    assert result == ["zeta", "epsilon", "delta", "gamma", "beta", "alpha"]

Output:

================================================== test session starts ==================================================
# details edited for privacy
collected 1 item                                                                                                        

04-lists/tests/test_reversing_lists.py .                                                                          [100%]

=================================================== 1 passed in 1.28s ===================================================
@BugDiver
Copy link

BugDiver commented Mar 13, 2022

This issue happens when using testbook with unittest module as well.
I can think of two passible aproaches to mitigate this issue (without breaking backward comapatiblity)

  • Add a named argument in testbook decorator which tells it to pass the notebook cleint as last argument instead of first (not a very clean approach i guess)
    example:
    class TestHelloWorldNoteBook(unittest.TestCase):
    
        @testbook(notebook_path, execute=True, pass_client_in_last=True)
        def test_say_hello(self, nb):
            say_hello = nb.ref('say_hello')
            self.assertEqual(say_hello(), 'Hello World')
  • Chage the decorator or create new one which acts as class decorator, and inject the client in class as class attribute
    example:
    @testbook("foo.ipynb", execute=True) # this inject `nb` in class
    class TestHelloWorldNoteBook(unittest.TestCase):
        def test_say_hello(self):
            say_hello = self.nb.ref('say_hello')
            self.assertEqual(say_hello(), 'Hello World')

@rohitsanj

@BugDiver
Copy link

@bytinbi Here is the workaround which worked for me

Use the testbook method as context manager instead of as a decorator

import unitest
from testbook import testbook

class TestFoo(unittest.TestCase):
    def test_foo(self):
        with testbook(path, execute=True) as nb:
            foo = nb.ref("foo")
            res = foo();
            self.asssertIsNotNone(res)

Altough the implementation is for unittest but I guess it applies to pytest as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants