30

I am quite a beginner in code testing, and was an assert whore before. One thing worrying me in unit testing is that is often requires you to make public (or at least internal) fields that would have been private otherwise, to un-readonly them, make private methods protected virtual instead, etc...

I recently discovered that you can avoid this by using things like the PrivateObject class to acces anything in an object via reflection. But this makes your tests less maintainable (things will fail at execution rather than compile time, it'll be broken by a simple rename, it's harder to debug...). What is your opinion on this ? What are the best practices in unit testing concerning access restriction ?

edit : consider for instance that you have a class with a cache in a file on disk, and in your tests you want to write to memory instead.

12
  • 2
    The python folks only have public. They're happy. Why worry? Why not make everything public?
    – S.Lott
    Commented Dec 22, 2011 at 14:15
  • 13
    Because that is far from best practices in the majority of the top languages. The real answer is to use good interfaces and such so you can use mock objects to perform testing against.
    – rlperez
    Commented Dec 22, 2011 at 14:24
  • 2
    @Rig: Python's not a top language? Interesting.
    – S.Lott
    Commented Dec 22, 2011 at 16:22
  • 10
    "it is far from best practices in the majority of the top languages" does not logically imply (or even allude that) "Python is not a top language".
    – Mike Nakis
    Commented Dec 22, 2011 at 16:26
  • 1
    Precisely, it is a top language but most of the top languages are not Python and endorse setting appropriate scope. I stand by my statement. There are patterns designed to make highly testable software while ensuring variables maintain scope. Even Python programmers tend to emulate scope with prefixes from what I have seen.
    – rlperez
    Commented Dec 22, 2011 at 17:33

5 Answers 5

41

You should never-ever have to

make public (or at least internal) fields that would have been private otherwise, to un-readonly them, make private methods protected virtual instead

  • Making a private member non-private turns the object into a Leaky Abstraction (wikipedia) which is the cause of much weeping and wailing and gnashing of teeth. (wiktionary)

  • Un-readonlying a field can make an immutable object mutable, which is nothing short of a disaster.

Your test should consider your object-under-test as a Black Box (Wikipedia), meaning that it should only be concerned with the public interface of the object-under-test, not with details of its implementation.

If an object cannot be sufficiently tested via its public interface, then you need to find ways to provide formal and useful extensions to its interface that would facilitate testing. For example, a registration system which only exposes a pair of Register()/Deregister() methods is what I call a Black-Hole Interface because information can only enter it but never leave. To fix this:

  • The solution is most definitely NOT to expose its internal collection of registrants, so that the test can go peeking into that collection.

  • The solution is to add an IsRegistered() method, even it will never be invoked by the application at hand; still, it is a formal and useful extension to the interface, it prevents the interface from being a black-hole interface, and incidentally, it accommodates Black-Box Testing.

The important thing is that changing the implementation of the object should not require you to change the unit test. In principle you should be able to completely rewrite the module under test, and reuse the existing test to make sure that the new version works exactly as the old version. This is only possible if the test is strictly a black-box test.

Amendment 2022-11-02 for further reading:

3
  • 2
    Exactly! If you can't unit test it without reflection or hacks, then you've probably got a mistake in your API or your design.
    – Falcon
    Commented Dec 22, 2011 at 17:08
  • Are you saying that making private methods protected virtual to help with mocking in tests is considered bad practise?
    – Robula
    Commented Jan 17, 2019 at 16:04
  • 2
    @Robula In my book, yes. I don't even write my tests in the same package as the production code, because I have no use for being able to access non-public members. I don't use mocks, either. Of course if you have to use mocks, then you are going to have to do whatever it takes to facilitate mocking, so protected virtual might be a practical compromise in many situations. But ideally a design has no need for mocks, only alternative implementations, and ideally testing is black-box testing, not white-box testing, so things are tested in integration, without mocks.
    – Mike Nakis
    Commented Jan 17, 2019 at 20:53
14

In C# you can use the InternalsVisibleToAttribute to allow your test assembly to see internal classes in the assembly you're testing. It sounds like you already know this.

In most cases I'm only interested in testing the public API of my assembly. In a case where I want to do white-box testing of some unit, I move the code to an internal class, and make it a public method of that class. Then I can code a unit test for that class.

This lends itself well to dependency injection and the strategy pattern. You can abstract the method into an interface, inject the interface into your parent object's constructor, and just test that the parent delegates appropriately. Then you can test that the child object behaves appropriately. This makes everything more modular.

8

In my experience it is best not to expose the internals of your class specially for the test. Even though this may appear to make the test easier to write. The test is the first client of your class, so if it is hard to test your class through its public interface, it will probably be hard to use your class in other places too.

How easy the class is to test through its public API is feedback on how easy the class will be to use. Think of the tests partially as a way to prototype the usage of your class.

Try to consider why your test needs to sense something about the class that is not available through the public API. Perhaps your class is doing too much and needs to be decomposed into a group of co-operating classes instead? Then you can mock some of the collaborating classes to sense what the class under test is doing.

Try to apply the dependency inversion principal and introduce interfaces between your classes which you can mock in your tests. You can provide the collaborators to your test by using inversion of control.

Also consider applying the "tell don't ask" principal to help structure your class interface in a robust, loosely coupled, manner, where the right information is exposed and the rest is hidden.

6

Personally I prefer to leave the code I am testing as pure as I can and if the test code needs hacks (and in C++ nasty pointers etc) I am happy to do that.

5
  • See programmers.stackexchange.com/questions/77313/…
    – Zonko
    Commented Dec 22, 2011 at 15:24
  • 1
    I agree, this is the way to do it. Keep the ugliness in the test code, where it can't hurt anything. Commented Dec 22, 2011 at 15:33
  • @AlanDelimon Might it not hurt the mind of subsequent maintenance programmers? Commented Dec 22, 2011 at 16:36
  • 1
    @flamingpenguin ugly code can hurt programmers anywhere; but ugly test code will likely do less harm since it will only affect people modifying the class and not also people who simply consume it. Commented Dec 22, 2011 at 19:32
  • 2
    @flamingpenguin It might, but if you have to put ugly code somewhere, it might as well be in a unit test where it's less likely to need modification and be part of the application. Commented Dec 23, 2011 at 2:02
6

The visibility of your code has nothing to do with your unit tests!

You make your methods private not with the purpose to hide them from testing and you don't make them public to expose for testing, right? This is your implemenation that is essential here, not the tests -- those server as proof of your code, but they are not your code.

There is a plenty of ways to enable access to hidden (private or internal) methods and properties. Many test frameworks and IDEs (for example, Visual Studio) support you here by generating everything needed for the access, you only have to author your test cases. So, why worry? Better keep your code clean and neat.

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.