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

Fix bug with usage of getattr to get the test name #38

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

gregbell26
Copy link

@gregbell26 gregbell26 commented Jan 4, 2024

In the event that an exception is raised during the class setup (setUpClass) using the JSONTestRunner with the built-in unittest module, the test runner crashes and fails to write to results.json.

Minimal example:

import unittest
from gradescope_utils.autograder_utils.json_test_runner import JSONTestRunner

class Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        raise AssertionError("This shouldnt crash the entire platform")

    def test1(self):
        pass


if __name__ == "__main__":
    tests = unittest.defaultTestLoader.loadTestsFromTestCase(Test)
    resultsPath = "./results.json"

    with open(resultsPath, 'w') as w:
        runner = JSONTestRunner(w)
        runner.run(tests)
Stack trace
  Traceback (most recent call last):
    File "/usr/lib/python3.10/unittest/suite.py", line 166, in _handleClassSetUp
      setUpClass()
    File "/home/gjbell/git/gradescope-utils/main.py", line 7, in setUpClass
      raise AssertionError("This shouldnt crash the entire platform")
  AssertionError: This shouldnt crash the entire platform
  
  During handling of the above exception, another exception occurred:
  
  Traceback (most recent call last):
    File "/home/gjbell/git/gradescope-utils/main.py", line 19, in <module>
      runner.run(tests)
    File "/home/gjbell/git/gradescope-utils/gradescope_utils/autograder_utils/json_test_runner.py", line 196, in run
      test(result)
    File "/usr/lib/python3.10/unittest/suite.py", line 84, in __call__
      return self.run(*args, **kwds)
    File "/usr/lib/python3.10/unittest/suite.py", line 114, in run
      self._handleClassSetUp(test, result)
    File "/usr/lib/python3.10/unittest/suite.py", line 176, in _handleClassSetUp
      self._createClassOrModuleLevelException(result, e,
    File "/usr/lib/python3.10/unittest/suite.py", line 236, in _createClassOrModuleLevelException
      self._addClassOrModuleLevelException(result, exc, errorName, info)
    File "/usr/lib/python3.10/unittest/suite.py", line 246, in _addClassOrModuleLevelException
      result.addError(error, sys.exc_info())
    File "/home/gjbell/git/gradescope-utils/gradescope_utils/autograder_utils/json_test_runner.py", line 136, in addError
      self.processResult(test, err)
    File "/home/gjbell/git/gradescope-utils/gradescope_utils/autograder_utils/json_test_runner.py", line 123, in processResult
      if self.getLeaderboardData(test)[0]:
    File "/home/gjbell/git/gradescope-utils/gradescope_utils/autograder_utils/json_test_runner.py", line 51, in getLeaderboardData
      column_name = getattr(getattr(test, test._testMethodName), '__leaderboard_column__', None)
  AttributeError: '_ErrorHolder' object has no attribute '_testMethodName'

results.json is not created, which causes the Gradescope autograder to fail when deployed.

Ideally, this should show the test failure in results.json similarly to how the built in TextTestRunner behaves:

E
======================================================================
ERROR: setUpClass (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/gjbell/git/gradescope-utils/main.py", line 7, in setUpClass
    raise AssertionError("This shouldnt crash the entire platform")
AssertionError: This shouldnt crash the entire platform

----------------------------------------------------------------------
Ran 0 tests in 0.000s

FAILED (errors=1)

The reason that this is occurring is because the how the getters called from buildResult and buildLeaderboardEntry are assuming that test will always has _testMethodName. And that is true, when test is an instance of unittest.TestCase. However, that is not the case in the error condition that I explained above (when test is actually an instance of _ErrorHolder).

This pull request fixes that error by checking to see if _testMethodName actually exists in test before trying to access it.

After those fixes, we see that we get similar output to TextTestRunner in results.json.

{
    "tests": [
        {
            "name": "setUpClass (__main__.Test)",
            "status": "failed",
            "output": "Test Failed: This shouldnt crash the entire platform\n"
        }
    ],
    "leaderboard": [],
    "execution_time": "0.00",
    "score": 0.0
}

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

Successfully merging this pull request may close these issues.

1 participant