Search code examples
c#.netunit-testingnunitprivate-methods

Testing classes with threads, events, and private methods


general consensus

I've done quite a lot of reading up on the subject of testing complex classes and private methods.

The general consensus seems to be:

  • "if you need to test private methods then you're class is badly designed"
  • "if your class is complex, then you need to separate it out"

So, I need your help.

the problem Class

So I have a relatively simple class whose long running job it is to:

  • poll a datasource
  • do some very simple mapping of the data
  • send that data somewhere else

Aditionally:

  • it needs to be able to be quite fault tolerant by being able to retry various tasks in case of certain errors.

the testing problem

The point of the class is to abstract a lot of the fault tolerance and threading... basically by using a simple Timer Class and some internal lists to keep track of errors etc.

Because of the Timer, certain methods are called on different threads asynchronously... additionally a bunch of the methods rely on global private fields.

How should I test this class... particularly because so many methods are private?

cheers guys


Solution

  • I would extract the code to poll the data into a separate class that can be mocked, and also extract the code that sends that data for the same reason. You might want to extract the data mapping code, depending on how trivial it is.

    I would definitely use mock timers in the unit tests, otherwise your tests are hard to set up and slow to run. You can either pass in the timer in the constructor, or expose a property that you can set. I often create a regular timer in the constructor and then overwrite that from my unit test.

    You might also be able to extract the retry logic so that it can be tested separately from the other code. Passing in a delegate of the code to try and retry might be a way to decouple the data code from the retry logic. You can also use IEnumerable and the yield statement to generate your data and feed it to the retry code. Essentially, I'm looking for ways to make it so that the retry code doesn't directly call the target code that it's supposed to try and retry. That makes it easier to test and generate all possible errors for, although you can get some of the same benefits by just mocking out that target code.

    If you really have multithreaded scenarios that you need to test, there are some tools out there to coordinate threads from within a test. One of them is a port I created of Java MultithreadedTC called TickingTest.