0

tldr; My unittests are running twice. Any idea why?

Consider that I have 2 files:

a.py
checker.py

I have a weird requirement for a.py. a.py has the following code and is required to have this code:

import checker

def factorial(n):
    if n == -1:
        raise Exception("Raised this exception on purpose")
    result = 1
    for i in range(1,n+1):
        result *= i

    return result

total = 53

checker.runTests()

I'd need to be able to run the tests for factorial in checker.py

import unittest


class MyTestCase(unittest.TestCase):
    def test_factorial3(self):
        import a
        self.assertEqual(a.factorial(3), 6)

    def test_factorial4(self):
        import a
        self.assertEqual(a.factorial(4), 24)

    def test_factorial5(self):
        import a
        self.assertEqual(a.factorial(5), 120)

    def test_factorialnegative1(self):
        import a
        self.assertEqual(a.factorial(-1), 0)

    def test_total(self):
        import a
        self.assertEqual(a.total, 53)
def runTests():
    runner = unittest.TextTestRunner(verbosity=2)
    suite = unittest.TestLoader().loadTestsFromTestCase(MyTestCase)
    runner.run(suite)

I'm open to suggestions on how to improve checker.py if there's any better ways to handle the circular dependencies. However, my issue is that when I run a.py, I don't get expected output, particularly the tests are run twice? See:

test_factorial3 (cisc106checker.MyTestCase) ... test_factorial3 (cisc106checker.MyTestCase) ... ok test_factorial4 (cisc106checker.MyTestCase) ... ok test_factorial5 (cisc106checker.MyTestCase) ... ok test_factorialnegative1 (cisc106checker.MyTestCase) ... ERROR test_total (cisc106checker.MyTestCase) ... ok

====================================================================== ERROR: test_factorialnegative1 (cisc106checker.MyTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/tnj/PycharmProjects/AutoExam/cisc106checker.py", line 21, in test_factorialnegative1 self.assertEqual(x.factorial(-1), 0) File "/Users/tnj/PycharmProjects/AutoExam/mattsapQ16.py", line 10, in factorial raise Exception("Matt Raised this exception on purpose") Exception: Matt Raised this exception on purpose

---------------------------------------------------------------------- Ran 5 tests in 0.001s

FAILED (errors=1) ok test_factorial4 (cisc106checker.MyTestCase) ... ok test_factorial5 (cisc106checker.MyTestCase) ... ok test_factorialnegative1 (cisc106checker.MyTestCase) ... ERROR test_total (cisc106checker.MyTestCase) ... ok

====================================================================== ERROR: test_factorialnegative1 (cisc106checker.MyTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/tnj/PycharmProjects/AutoExam/cisc106checker.py", line 21, in test_factorialnegative1 self.assertEqual(x.factorial(-1), 0) File "/Users/tnj/PycharmProjects/AutoExam/mattsapQ16.py", line 10, in factorial raise Exception("Matt Raised this exception on purpose") Exception: Matt Raised this exception on purpose

---------------------------------------------------------------------- Ran 5 tests in 0.005s

FAILED (errors=1)

However, when I run it from checker.py, it's only run once. What is going on? Why is it running twice?

7
  • Why define runTests at all? Use python -m unittest discover, and it will find and run your tests. Commented Jan 25, 2018 at 22:56
  • Also, is the indentation correct in your code? It looks like a few things at the end of checker.py are indented too much. Commented Jan 25, 2018 at 22:57
  • 2
    BTW, you can replace your __import__ lines with import a, and you should fix the references to x to be a throughout the tests. Commented Jan 25, 2018 at 23:04
  • checker is a module given to intro to CS students. runTests is a wrapper so they don't get confused and too heavily involved in command line/shell etc, it's just something they can copy paste to see if their code works. I've fixed the indent, it copied pasted incorrectly. Commented Jan 25, 2018 at 23:05
  • There's still runTest indented wrong. If this scaffolding has been given to you, then it looks like you are meant to run checker.py, since it has the __main__ clause in it. Either you have misunderstood some part of this assignment, or the person who wrote the assignment needs to find a new job. Commented Jan 25, 2018 at 23:07

3 Answers 3

3

Maybe the better thing to do is to ask, "What is the best scaffolding I can give to my students so it's easy for them to write and run tests?" They will be editing both the product file (a.py) and the test file (checker.py), so why choose one over the other? Have them run checker.py to run the tests. Then you won't be introducing import loops, and they will have a simple structure they can understand.

Sign up to request clarification or add additional context in comments.

1 Comment

Good point. Actually, they wouldn't be editing the checker.py. The checker.py provides some base tests to let them know if they're on the right direction. checker.py has some things that allow students to submit their code (ftp) to some server. They write their own tests in a.py.
2

The snippet outside of you function in a.py

total = 53 
checker.runTests()

Is executed once when you run a.py and then again when you import it in checker.py. So runTests() is executed twice.

8 Comments

Sorry. I can't modify a.py. However, in the import statements in checker.py, if what you are saying is true, why isn't it run 6 times (once per test's import + the runTests call )?
@mattsap it's only actually imported once. Once it's imported, repeated calls for import are ignored
That makes sense! Any idea how to get rid of the second call other than removing it from a.py?
@mattsap I answered you question to why it runs twice since others have presented different ideas on how to avoid this problem. But it's hard to give a solution which would fit your case since you have a few and not very clear limitations on what should not be touched and how it should work.
I'd like to assume that a.py can't be modified at all and checker.py can be as ugly as needed since no body (other than me) will see it.
|
1

Short version: Get rid of the import checker at the beginning and of the checker.runTests() at the end of the file with the solution.

Longer version:

By having a call to the tests inside your "production code" you are attaching your code to the tests. And you don't really want to do this.

To run your tests you could change the end of the tests file to

if __name__ == '__main__':
    unittest.main()

and run the tests by calling python my_test_file.py.

Following the step described in the short version will also solve the circular dependencies issue you mentioned.

Another easy-to-do improvement: You can write import a in the second line and get rid of all the duplicated import a lines.

4 Comments

Actually, a.py should be the main and that file has to call the checker.runTests() at the end. It's a silly requirement that actual development would never incur, but it's for beginning students who have a difficult time following directions.
It's ok. Everyone was once a beginner. But keep in mind that the code being tested should not know about the existence of the tests.
Right. I wanted to let the students just run the file they are currently working in. Do you have a solution as to how to meet this requirement?
I wouldn't encourage this since it's not what's expected in a real world job.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.