4

My company just started to practice Unit Testing (MS Test) on an ever-enhancing legacy system.

The system is build with a core functions and developers are developing new features (by adding new Class Library) and integrating to core system through interface such as Overriding or Implements method from the base of core functions. Each of the Class Library we have developed has a standard method ProcessRequest and is a protected override method, that has codes that does processing such as calling to external web service supplier, assigning response object and etc.

However, In each of the individual class library may/may not has utility function methods such as ConvertValue(), AssignValueToObject() method etc.

As what we have raised in a heated argument/discussion, there are people a small group of developers suggest that we only test Utility Function Methods (i.e. ConvertValue(), AssignValueToObject()) whilst ignoring the need to test the code in protected override ProcessRequest() method and suggest that we should only test ProcessRequest () exception handling.

However, a few of the colleagues do support to test even in ProcessRequest() method as well.

My leader is also saying testing ProcessRequest() is "too high level" but I would think that a Unit Test should conduct from the entry point of the new features.

Please advice on the best practice point of unit test entry which is practice in the industry / development culture.

Note:-

  • There are changes-prone situation on ProcessRequest() whereby a supplier might change their WS endpoint which we requires changes and update on our ProcessRequest() as well.

  • Majority of the Result Assigning code is in ProcessRequest().

  • Has no intention to conduct Unit Test on Legacy code (core system) at the moment as it is too large to be covered. Refactoring codes on legacy codes is not an option.

0

3 Answers 3

1

Writing unit test on a legacy code is daunting task. Since you are just starting to implement unit test, its better to start simple and on new modules you are developing. Later you can apply the learnings on the older modules.

The code should be in such a way it allows unit testing. Mainly the class you want to test should support mocking and external components/classes which are not part of the current class should be derived from interfaces. So that you will be just doing unit testing or else you will be doing integration testing of all the related components.

To make it unit testable you might need to touch all the related classes and re-write them so that it can be testable. Any change will be a breaking change unless you have test harness for them. If you are new to unit testing, you need to get comfortable to write unit test in line with your project.

This would be better if you start on the newly created components. Then you can be confident of the practice first then you can apply this learnings on the legacy code.

Edit

When you are confused on which methods to write unit tests in a particular class, you can follow these two points.

  1. TLDR; You are not testing methods of a class, you are testing functionality.

    A class's functionality is exposed by its public method and not its private methods. Every public method should have its own unit test method. If you have only ProcessRequest as public method, your test case should address only ProcessRequest's functionality nothing more. You should test the class as an outsider, you should not be aware of private members/private methods. You should test only publicly visible properties and methods. As a general rule don't make members public for the sake of testing, there can be exceptions. But functionality should be testable without peering into internal state of the class.

  2. Every conditional branches in a method should have its own unit test. Which is roughly equal to - test case for each condition in if..else,switch..case, exception handling etc...
2
  • I have been working on the system for a year by now and roughly knows how does it really calling each other (Thanks to F10 and F11 on VS IDE). But it has been a continual battle of what should we test and the fact is the team is roughly new towards unit test and having resistant to change as well. I hope to bring in the correct ideology about unit testing and let it to be a standard practice for everyone, for current and future new starters.
    – Simon Loh
    Commented Nov 26, 2015 at 7:04
  • Goes back to my concern is the smaller functions within the class might not be as important as the main ProcessRequest() method and personally feel that it should not be neglected in the practice when we continuously adding new features. Correct me if I'm having wrong mindset.
    – Simon Loh
    Commented Nov 26, 2015 at 7:11
1

That's the thing about useful legacy systems. They are so useful, people want to keep them around, even as changes occur to user's needs, and to the environment of platform and middleware that they live in.

For this reason - the sure knowledge that there will be changes to the system, now and in the future - unit testing should be principally and almost exclusively concerned with the black box testing of classes and objects.

White-box testing of object internals means the tests break every time you refactor the code or change the system's functionality. This makes

  • the test code very fragile
  • the test results indicative rather than re-assuring
  • the test code very expensive to maintain and keep in lock-step with the code-base it provides testing services to

These are the things you need to have unit tests for:

Assertions on incoming messages - for both query and command messages.

Assertions should be just either side of each of the boundary conditions, and also a mid-point in the range of acceptable data. (= 5 assertions per method, for a method that acts on a range of integers)

Therefore a set of 5 assertions per valid incoming message in the Class's protocol.

Expectations on outgoing command messages, using a Mock object that has a message protocol matching that of the live object it is sending to.

(Command messages are ones which change the state of an attribute, aka have a side-effect).

Other tests do not demonstrate anything useful, and/or are too fragile, and require enormous amounts of effort to create and maintain.

Here is a very persuasive discussion video of exactly this topic, with worked code examples in Ruby.
The Magic Tricks of Testing (link)

0

It sounds like your company is actually doing a lot of integration tests with a unit test framework which is in your case MS Test. There is absolutely nothing wrong with this approach. However I regularly observe that discussions about unit testing get into a wrong direction when there is not a clear understanding of unit testing.

There are a lot of resources explaining what unit tests actually are. In a nutshell, in object-oriented languages, it is a test of a method with as less dependencies as possible.

I suspect that the code base consist of rather large classes with one ProcessRequest() method which calls private or internal utility methods. Under these circumstances (and the factors you described) testing those utility methods is closer to unit testing. You should have a lot of them. Testing ProcessRequest() methods are closer to integration tests and you should have a representative amount of them on top of many unit tests.

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.