2
\$\begingroup\$

I tried to initiate myself with unit testing the simplest way I can. What do you think of this way to begin with? Is it a clean way to proceed? Are there any major caveats to avoid?

NB:

  • Since I want to test a small script for each leetcode problem, I preferred to have the Test inside the script to be tested (and not in a different file).
  • I decided to use f as a global variable inside the main scope (I could have defined it in Test as a class variable and use self.f).
  • I create a function test for each case.
import unittest

class Solution(object):
    def islandPerimeter(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """


if __name__ == '__main__':
    import unittest
    f = Solution().islandPerimeter

    class Test(unittest.TestCase):
        
        def test_multiple_path(self):    
            self.assertEqual(f( [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]), 16)
         
        def test_one_square(self):
            self.assertEqual(f( [[1]]), 4)

    unittest.main()
\$\endgroup\$
2
  • 2
    \$\begingroup\$ I recommend putting unit tests in a separate package called tests in your project's root. This way, tools like pytest will automagically find and run appropriate tests. \$\endgroup\$ Commented Apr 26, 2022 at 10:33
  • 2
    \$\begingroup\$ For simple tests like this, consider using doctest to embed the tests within the function's docstring. \$\endgroup\$ Commented Apr 26, 2022 at 10:35

1 Answer 1

2
\$\begingroup\$

What do you think of this way to begin with?

Meh. Not great, but definitely better than nothing.

Is it a clean way to proceed?

No.

Prefer one of these. Hopefully you'll choose the first one.

  1. Put tests in a separate module (separate file), and import so you can call into the Solution class.
  2. Put tests in a Test class, as you've done here, which is up at module level and not protected by a __main__ guard.

Only the unittest.main() invocation should be protected by the guard. All the class / function defintions should appear at top level.

The other big change you made was: f = Solution().islandPerimeter

We could name such an alias f, fn, whatever. I don't find the aliasing helpful, but I suppose that you do, so cool. The downside is we're hanging onto a certain Solution instance, which isn't a big deal for this test suite but it might be troublesome for another codebase.

Prefer to use def setUp(), as documented. That will help to ensure that your various tests are independent of one another. Any state which might be accumulated by executing test_multiple_path() should not e.g. affect test_one_square(). Put another way, if we run all tests in one arbitrary order or in some other permutation, we should obtain identical results, due to them being independent of one another with no shared state.

\$\endgroup\$
1
  • \$\begingroup\$ 3. Put tests in the docstrings, so you can python -m doctest *.py. \$\endgroup\$ Commented Apr 28 at 15:58

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.