3

When I write (unit) tests I always try to avoid for example microsoft fakes because then the edit&continue feature no longer works. However this often requires refactoring... and sometimes methods or properties that exist only to support (unit) tests. An example:

With fakes I could do in each test method simply this:

ShimMyClass.InitializedGet = () => false;

but without fakes I need to write an internal method ResetInitialized() visible only to the (unit) test assembly.


As to:

I won't make any private methods public to support unit testing but write them only for unit testing. Such methods would not have any other pupose but to be called in a unit test. ResetInitialized would never be called during a normal workflow but only by a unit test.

In other words: if I didn't create the unit test I would never have to write this additional helper method/property. It will never be called by anything other then a unit test.


Example

I have a class that I initialize when the application starts and subsequent initializations are skipped. The initialization takes place internally when the first feature is used. In this case I'm not able to test more then one test case at a time. I need to be able to reset it to let the tests run automatically.

Would it be bad to make it actually also resetable but the only user of this feature would be the unit test?

2
  • It's not clear from this question why you're having to do this at all. Could you give a (more complete) example? Commented Nov 6, 2015 at 13:17
  • Sorry, still not really clear on that example. How is that class initialized? Is a method called on it with some data? And where is it initialized from? Is it just done from Program.cs or does some other class have that responsibility? Commented Nov 6, 2015 at 13:37

3 Answers 3

2

In other words: if I didn't create the unit test I would never have to write this additional helper method/property. It will never be called by anything other then a unit test.

A quick answer - you should not modify your source code to cater your tests.

Also rule of thumb: if a piece of code/method/class/functionality will never be called - remove it. Don't comment out, don't make it private - remove it.

However it is perfectly fine to write new mocks/stubs or fake method in your tests that would monkeypatch/overload/replace the original functionality.

Testsing/implementation should follow 80/20 rule meaning that approximatelly 80% of your development time will go on writing tests (unit, intergration, end-to-end, call any..) And it is completely obvious that if you are chaning the interface of your methods, then you will have to update your fakers or unit test structure.

2

Another answer: yes, you should absolutely modify your source code to cater to your tests.

Testing fulfills the function of improving and maintainig the quality of your code base. Therefore, making tests easier, or possible, to write, is a valid goal with positive business value, just like writing business code. It's logically inconsistent to spend time writing tests and simultaneously claiming that it is never justifiable to change business code for the sake of the test suite.

As always, this is a matter of trade-offs. Obviously you shouldn't make your code much less maintainable for a small improvement in test writing. But a small or zero expansion of the business code for a big improvement in test writing can absolutely be justified.

Lots of developers make the mistake of thinking that since test code isn't shipped to the customer, it's somehow not really part of the code base and needn't be as well-written and reliable as the business code. This couldn't be more wrong; we do testing solely because it helps us create better programs, so it cannot have zero value - otherwise it would be negligent of us to write any tests at all.

3
  • 2
    Reading between the lines on this question I think it's asking about adding methods like internal void SetStateDirectly(State state) or similar kinds of "backdoors" to classes, for testing. These are generally a sign of a design issue (like failing to do DI) Commented Nov 6, 2015 at 13:19
  • @BenAaronson I'd say you're right ;-) For example when I have a class that is initialized when the application starts and subsequent initializations are skipped I cannot test it in another test method because I cannot initialize it again. I need a way to reset it's state.
    – t3chb0t
    Commented Nov 6, 2015 at 13:24
  • @t3chb0t Possibly you could ask a question like "How can I avoid using fakes to test this code?" with a specific example illustrating the situation. Then it might be clear if doing something like changing how/where/when the initialization is done could work Commented Nov 6, 2015 at 13:27
2

You need to make your code testable. That's rule number one. Anything else is pandering to the rabble who say, "we don't need no stinkin' tests."

If you need to re-initialize something, there is a possibility that the lifecycle for your code-under-test is the problem. When you create a new object instance, does it not cleanly initialize to the state you want?

If you have hidden state, you may have to add state-monitoring variables that are only used for testing.

2
  • 2
    The answer to "How can I modify this bad design, so that I can test it?" is "Remove the bad design!".
    – Bent
    Commented Nov 6, 2015 at 13:37
  • The first step to improving a design is to make it testable. If you are handed a bag of code, often the only practical way to "remove the bad design" is to redesign it in place. Commented Nov 18, 2015 at 12:50

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