18

After reading about testing private methods in Python, specifically referring to this accepted answer, it appears that it is best to just test the public interface. However, my class looks like this:

class MyClass:

  def __init__(self):
    # init code

  def run(self):
    self.__A()
    self.__B()
    self.__C()
    self.__D()

  def __A(self):
    # code for __A

  def __B(self):
    # code for __B

  def __C(self):
    # code for __C

  def __D(self):
    # code for __D

Essentially, I created a class to process some input data through a pipeline of functions. In this case, it would be helpful to test each private function in turn, without exposing them as public functions. How does one go about this, if a unit test can't execute the private function?

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
darksky
  • 20,411
  • 61
  • 165
  • 254

3 Answers3

57

Python does some name mangling when it puts the actually-executed code together. Thus, if you have a private method __A on MyClass, you would need to run it like so in your unit test:

from unittest import TestCase

class TestMyClass(TestCase):
    def test_private(self):
        expected = 'myexpectedresult'
        m = MyClass()
        actual = m._MyClass__A
        self.assertEqual(expected, actual)

The question came up about so-called 'protected' values that are demarcated by a single underscore. These method names are not mangled, and that can be shown simply enough:

from unittest import TestCase

class A:

    def __a(self):
        return "myexpectedresult"

    def _b(self):
        return "a different result"


class TestMyClass(TestCase):

    def test_private(self):
        expected = "myexpectedresult"
        m = A()
        actual = m._A__a()
        self.assertEqual(expected, actual)

    def test_protected(self):
        expected = "a different result"
        m = A()
        actual = m._b()
        self.assertEqual(expected, actual)
        # actual = m._A__b() # Fails
        # actual = m._A_b()  # Fails
Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
16

First of all, you CAN access the "private" stuff, can't you? (Or am I missing something here?)

>>> class MyClass(object):
...     def __init__(self):
...             pass
...     def __A(self):
...             print('Method __A()')
... 
>>> a=MyClass()
>>> a
<__main__.MyClass object at 0x101d56b50>
>>> a._MyClass__A()
Method __A()

But you could always write a test function in MyClass if you have to test the internal stuff:

class MyClass(object):
    ...
    def _method_for_unit_testing(self):
        self.__A()
        assert <something>
        self.__B()
        assert <something>
        ....

Not the most elegant way to do it, to be sure, but it's only a few lines of code at the bottom of your class.

Community
  • 1
  • 1
BenDundee
  • 4,389
  • 3
  • 28
  • 34
  • 35
    You should not be putting test code inside production code. – Nathaniel Ford May 03 '18 at 22:08
  • 2
    this is super dangerous way to write your code - there should never be any code inside your production value that is used for testing - at the very least it will provide false positives, at the worst it could lead to said code accidentally being run in production. Please anyone doing a search for this do not use this answer. – lynkfox Feb 28 '22 at 18:36
0

Probably you should just test the run() method. Most classes will have internal methods -- and it does not really matter in this case whether or not all the code in __A(), __B(), __C,() and __D() is actually in run() or not. If you suspect or find problems, then you might want to switch to your debugger aspect and test the private methods.

pydsigner
  • 2,779
  • 1
  • 20
  • 33
  • In this case, I would need to initialize my class with the variables, call run, and test the outcome. But what if I want to test each step in run (each step == each function)? – darksky Mar 16 '13 at 19:12
  • Then you have to access them as BenDundee explains. – pydsigner Mar 16 '13 at 21:09
  • Since I received a downvote for this answer, I'd like to point out that I had addressed the question as asked: "In this case, would it be best to test the private functions and not test the public run() function?" – pydsigner Aug 20 '19 at 22:33
  • 1
    You were essentially suggesting he skips testing subroutines, though, or only test them manually. This is not a good habit to adopt; unit tests should test as tight a sequence as possible, and your tests (unit and integration) should test with as much coverage as possible. Knowing how to test private and protected methods is therefore useful, and the advice to skip doing so not great. – Nathaniel Ford Aug 17 '20 at 20:54
  • OTOH, unit tests should test _behaviour_, not _implementation_, so tests should exercise the public API, not private methods. – snakecharmerb Aug 12 '21 at 19:38
  • 1
    even Private methods should be tested via behavior - testing by behavior *Does not* mean testing from the API end point. It means that the behavior of the unit being tested is tested, not the data being passed to it. Private/Protected functions should be behavior tested and if you have trouble testing them, then you should reconsider how you write your code to make it easier to test. – lynkfox Feb 28 '22 at 18:37