In my previous post on unit testing, I explained how to handle a few simple situations, nevertheless things often get more complicated. One such complication occurs if the class that you want to test uses other objects. It may be a problem for several reasons – it’s code that we don’t want to test in the moment (because it would become too complex), it depends on something that we can’t control by our test (e.g. values returned from an external system), it’s too slow and so on.
The solution is to remove tight coupling between units and allow substituting dependency objects with something provided by the test (= dependency injection). Following these design principles will also make your code more flexible and easier to maintain, therefore designing code for testing will force you to make your code better in several aspects.
Let me show you a very simple example, where I have a single dependency on database:
public Amount availPhysical(ItemId _itemId, InventDimId _inventDimId){ InventSum is = InventSum::find(itemId, inventDimId); return is.PostedQty+ is.Received- is.Deducted+ is.Registered- is.Picked- is.ReservPhysical; }
If we want to test this method with different values, we would have to save new values to database before running each test. It can be complicated due to references to other tables, and changing data in database would influence everybody using the same data, including other running tests. There are some ways how to do it, but we’ll now try to avoid database completely. Also note that operations in memory are much faster than accessing database, so you can run huge amount of tests in very short time.
If you look at the method, you’ll notice that it does two different things – it finds an InventSum record and it does the calculation. If we split it to two methods, each with its own responsibility, testing the calculation becomes trivial.
// The complicated calculation logic was extracted to this methodpublic Amount calcAvailPhysical(InventSum _is){return _is.PostedQty+ _is.Received- _is.Deducted+ _is.Registered- _is.Picked- _is.ReservPhysical; } // The remaining code is herepublic Amount availPhysical(ItemId _itemId, InventDimId _inventDimId){return this.calcAvailPhysical(InventSum::find(_itemId, _inventDimId)); }
Now we can easily write tests like this:
InventSum is; is.Received=8; is.ReservedPhysical=5; this.assertEquals(3,new MyClass.calcAvailPhysical(is));
This covers calcAvailPhysical(), but what about availPhysical()? Honestly, I don’t think it’s worth investing time into. It calls only two methods, without any conditions or anything. The only thing it could go wrong is using wrong parameters for find() and it may be covered by code review and other types of testing.
You might say that you to want cover all code by unit tests and it would indeed be nice, but on the other hand, you have to think about the price and whether you really want to test the data access layer at all. This example demonstrates the approach I prefer – extracting complicated pieces of logic and test them thoroughly. The parts that are difficult to test are kept trivial and unlikely to change very often.
There surely are cases when testing against database is needed, such as when the most important logic to test is a database query. I’m going discuss database access in a later post.
Let’s look at a few other cases involving dependencies.
Maybe you have quite a few values you would have to pass to your method and the number of parameters would grow quickly. You can always create a class functioning as a data container and use it instead of many parameters (think of data contract – it’s the same design pattern).
In the example above, the method got everything in parameter. But it’s often not the best design; another variant is setting dependencies to object’s state.
Here I have a little bit more complex example. I’m going to test ClassToTest; unfortunately it uses DependencyObject which I don’t want to cover by my test.
class ClassToTest { DependencyObject depObj; new(){ depObject = DependencyObject::construct(MyParameters::find().DependencyType); }}
DependencyObject is instantiated inside ClassToTest, therefore my test can’t control it. Furthermore, it’s initialized in different ways depending on a parameter in database, therefore my test would have to know about this implementation detail and change the parameter as needed (with all the troubles mentioned before).
Let’s just slightly refactor the class. We’ll allow setting depObj via a parm* method, while keeping the initialization logic in place in a factory method:
class ClassToTest { DependencyObject depObj; // Caller code can set the dependency through this methodpublic DependencyObject parmDependencyObject(DependencyObject _depObj = depObj){ depObj = _depObj; return depObj; } // The class still offers a method for initialization from the parameter,// but it's now just one of possible ways.publicstatic ClassToTest newDefault(){ ClassToTest c =new ClassToTest(); c.parmDependencyObject(DependencyObject::construct(MyParameters::find().DependencyType)); return c; }}
Now tests can create DependencyObject in any way they need and they don’t have to use the parameter table at all.
ClassToTest c =new ClassToTest(); DependentObject d = ... c.parmDependentObject(d); ... act and assert ...
This makes the solution more flexible too – if you have a new requirement that needs depObj created in some other way, you can use reuse the class without any change. This is yet another example where designing for testability leads to more robust architecture.
Note that you don’t want to always expose all dependencies to caller code, because it would reveal implementation details that should stay hidden in the class. As with any other development, you have to weigh many aspects when designing architecture of your program. By the way, if you write .NET components, you can hide such logic inside assemblies and access internal members from tests with the help of InternalsVisibleTo.
The technique shown above work great if all what we need is to inject specific values to the tested unit, but sometimes we want to inject different behaviour. In many cases, we want simply to “turn off” some logic interfering with the test.
For example, doStuff() runs some logic I want to test and it prints a report.
void doStuff(){ ... some interesting logic ... reportRun.run(); }
To avoid all potential troubles with printing inside test, we can replace actual reportRun with a custom subclass overriding run() method and doing nothing:
MySRSReportRunDoingNothing extends SRSReportRun {publicvoid run(){// Nothing here}}
The test will inject the dummy object (so called test stub) into the class under test:
ClassToTest c =new ClassToTest(); c.parmReportRun(new MySRSReportRunDoingNothing()); c.doStuff(); ... assert ...
By the way, there may be better ways for designing the code – for example, I could extract the logic to test into a separate method or add a flag controlling whether the report should be printed or not. Therefore if you find yourself writing many test stubs, maybe you should focus more on design for testability.
You can do much more with these test stubs. Instead of disabling all functionality as above, you can actually configure them to do whatever is useful for your test. For example, they can simulate access to database or calls to web services and return exactly what you need:
class WebServiceTestStub {publicstr getData(str _id){if(_id =="Test1"){return"Data expected by a specific test case"} ... }}
You can also use these objects to verify whether system under test did what it should have done. For example, you test a piece of logic that should call posting. You don’t want to run the posting itself, but you want to verify that it got called (and didn’t get called if a validation failed). You can create a so-called mock object to remember that post() was called and then write assertions for it.
ClassToTest c = ... c.parmPosting(new MockPostingSubSystem()); c.run(); this.assertTrue(c.postWasCalledOnce());
This goes way beyond mere isolation – it’s a quite different type of testing.
By the way, you can find sophisticated mock frameworks for other languages, which greatly simplify working with these objects. You don’t have to create them by yourself; you can just configure what type you want to mock, what’s the expected behavior and so on.
Just to give some idea, the following test (written in Java with jMock) creates a mock for Subscriber class and defines that its receive() method should be called with a given message. The framework creates the mock object for you at runtime and verifies the expectations. If receive() isn’t called exactly once or the parameter is wrong, the test will fail.
publicvoid testOneSubscriberReceivesAMessage(){// set up Mock mockSubscriber = mock(Subscriber.class); Publisher publisher =new Publisher(); publisher.add((Subscriber) mockSubscriber.proxy()); finalString message ="message"; // expectations mockSubscriber.expects(once()).method("receive").with( eq(message)); // execute publisher.publish(message);}
Some mocking frameworks allow things like mocking static methods, therefore you could take a piece of code (written without taking testing into account) and mock out inconvenient find() methods, for example. Unfortunately we don’t have any such framework for X++ (Ax Dynamics Mocks never got far enough), but my hope is that we’ll get it (or build it) one day. AX 7 will give us new ways how to achieve it.
Nevertheless mocking frameworks are by no means necessary for unit testing – they just make things more convenient.
When you’re writing a class with a dependency, it’s worth to consider using interfaces instead of specific classes. First of all, it makes your code really flexible without unnecessary restrictions. For example, if you need logging in your class, you need just some object that can accept a logging message and you don’t really care about what kind of object it is. Therefore create an interface and use it as the type of your dependency:
interface ILogger{void logMessage(Exception _ex,str _msg)} class MyClass { ILogger logger; }
That’s a nice, robust implementation – you can easily replace one way of logging with another and you can even introduce things like composite loggers calling several other loggers – all without ever changing your class. But it also means that you test stub doesn’t have to inherit from an existing logger – it can be any class implementing the right interface. It’s much easier, because if your test stub inherits code from a parent class, you often have to be very careful to override all methods called by the class under test. With interfaces, the compiler tells you what to implement and you’re sure you don’t inherit anything you don’t want.
You can see that it’s all about designing code in a way that make testing easy. People new to unit testing often don’t realize it and try to test virtually untestable code. It’s no surprise they find it awkward and complicated – they should refactor the code first before testing it. This is an obviously problem with a lot of standard code in Dynamics AX, because you can’t test it and you also don’t want to change it (to avoid upgrade conflicts in future). My advice may be surprising: just don’t it. Test your code and not code developed by somebody else, such as Microsoft and ISVs. Yes, you often modify standard code, but if you keep things separated, you can test your code by unit tests and leave the integration with standard code to a different type of testing. It works really well for me – give it a try.
Although we’ve covered quite a lot today, there are whole books on this topic (even I would have much more to say) and a single blog post can only scratch the surface. I hope it makes some sense to you, because I think it’s what people often miss when starting with unit testing – and why they ultimately give it up. They quickly learn things like assertion methods, but never get to know how to design complex code that still can be decomposed to units for unit testing. I showed you a few basic techniques, but it’s mainly for demonstration. As with any other code, you have to use to use your own developer skills to design the best solution for your particular situation.