I am trying to test-drive (or at least write unit tests) my Model classes but I noticed that my classes end up being too coupled. Since I can't break this coupling, writing unit tests is becoming harder and harder.
To be more specific:
Model Classes: These are the classes that hold the data in my application. They resemble pretty much the POJO (plain old Java objects), but they also have some methods. The application is not too big so I have around 15 model classes.
Coupling: Just to give an example, think of a simple case of Order Header -> Order Item. The header knows the item and the item knows the header (needs some information from the header for performing certain operations). Then, let's say there is the relationship between Order Item -> Item Report. The item report needs the item as well. At this point, imagine writing tests for Item Report; you need have a Order Header to carry out the tests. This is a simple case with 3 classes; things get more complicated with more classes.
I can come up with decoupled classes when I design algorithms, persistence layers, UI interactions, etc... but with model classes, I can't think of a way to separate them. They currently sit as one big chunk of classes that depend on each other.
Here are some workarounds that I can think of:
- Data Generators: I have a package that generates sample data for my model classes. For example, the OrderHeaderGenerator class creates OrderHeaders with some basic data in it. I use the OrderHeaderGenerator from my ItemReport unit-tests so that I get an instance to OrderHeader class. The problem is these generators get complicated pretty fast and then I also need to test these generators; defeating the purpose a little bit.
- Interfaces instead of dependencies: I can come up with interfaces to get rid of the hard dependencies. For example, the OrderItem class would depend on the IOrderHeader interface. So, in my unit tests, I can easily mock the behaviour of an OrderHeader with a FakeOrderHeader class that implements the IOrderHeader interface. The problem with this approach is the complexity that the Model classes would end up having.
Would you have other ideas on how to break this coupling in the model classes? Or, how to make it easier to unit-test the model classes?
Update after going through the answers:
- I have done a little more thinking on this problem and noticed that some of these dependencies were not really necessary (as most answers pointed out). The reason why they existed is because my UI layer needed it. Basically, I have inserted these dependencies so that UI layer can easily navigate from child to the header and display some header fields at the child level views. Looking at it now, it looks so simple; however it took some time for me to figure out why these dependencies existed in the first place. Now, the UI will basically keep the header in its context when navigating to the child level views.
- On the other hand, I have removed some of the dependencies via interfaces. These dependencies were mostly one-to-one; for example between Header and HeaderReport. Previously, HeaderReport depended on Header for retrieving information (items, date, etc...) and this made HeaderReport very hard to test. Now, the HeaderReport depends on the HeaderDataProvider interface and I am able to achieve complete isolation. HeaderReport is very easy to test with stub classes for the HeaderDataProvider interface.
- It looks easy to create these abstractions (UI Layer - Model Layer - Backend Layer), however in reality, it really takes a lot of practice!