We model our business domain with hundreds of interconnected objects and complex behavior emerges.
As the complexity grows it becomes less feasible for us to know what is really going on when the system behaves in unexpected ways- there are too many relationships and rules to keep in our head at one time. Unit testing help us manage this complexity by protecting us from regressions, and encouraging us not to force too much responsibility onto our objects. Unit tests also maintain knowledge about the assumptions of the objects and their relationships so we don’t need keep it in our head.
In this two-part post, I’m going to focus on unit test writing for Cairngorm Commands, and some techniques I’ve started using for faking dependencies when unit testing.
Commands are Core to Cairngorm Development
I use the Cairngorm micro-architecture for much of my Flex application development. The Command pattern is central to how Cairngorm facilitates collaborations between objects when executing a particular unit of work. Commands collaborate regularly with the following other types of objects:
- The Events that dispatch them
- The Models that they manipulate
- The Business Delegates they delegate work to
By their very nature they have many dependencies, so writing unit tests for Commands can be a challenging and labor intensive endeavor. It is very easy for these Commands to have too much Fan Out and become overly complex.
Managing Command Complexity is key to reducing Cairngorm Application Complexity
The interconnections between Commands and other objects make testing more difficult, but this is good work to do- it is worth the investment. Commands are at the nexus of much object collaboration, and they map so directly to the actual growing business logic (or illogic) of the system. If we can get in there and test them properly, we will arrive some of the most effective application coverage.
This isn’t to say that most of the application’s logic should live in the Command. I wouldn’t advocate for that. To the contrary, I think the Model is an ideal place to move most of our business logic. But by their very nature, Commands tie the whole system together by bridging the gap between MV & C.
On a side note, I don’t think that Commands should have any relationship with Views. I’m a strong advocate for Model-Driven Cairngorm as a good approach to separating concerns in the application. There should be no know-of relationships between Command and views if it can be avoided.
Effective Unit Testing of Commands is a tool to reducing Command Complexity
Aside from effective coverage, a secondary gain to unit testing Commands is in how they help us refactor our code to reduce complexity. It is easy to make them overly complex and writing unit tests encourages us toward simplification.
I think it is fairly easy to make Commands into Transaction Scripts. The Delegate has just given us some results, and we have access to the model so why not just update it directly? Our job is done, right?
Problems arise as the business logic surrounding updating the business model evolves very often organically. Business logic in our Commands becomes complex very easily and can be very conditionally nested. What is happening here is that our Commands are being forced to do too much work– we are asking them to know too much about the model.
I see an amalgam here with what Martin Fowler discusses in Patterns of Enterprise Application Architecture:
Using a Domain Model, as opposed to Transaction Script, is the essence of the paradigm shift that object-oriented people talk about so much. Rather than one routine having all the logic for a user action, each object takes a part of the logic that’s relevant to it.
We want to move away from having Transaction Script-esque Commands, and start to do some proper Domain Modeling. We are Object Oriented developers after all, right?
Writing Transaction Script unit tests is much more labor intensive because I find that there tends to be more dependencies and contexts you need to fake. If we move toward a Domain Model we more evenly distribute responsibility away from the Command and into the Model and our unit testing because much easier.
Up Next, code examples
Now that we have set the stage for why we want to invest in strategies for unit testing our Commands, in my next post I will give concrete code example of some ways to do it including a nice AS3 Mock Object framework I’ve just started using.