Reputation: 666
For my last Java project I unit tested all classes. Each class had it's own interface and implementation (ie. Person and PersonImpl). Classes interacted with each other via interface only and I used mocks to test each class independent from the others. It ended up really being a pain because it broke my eclipse ctrl+click navigation since every method call is called on an interface. It also cluttered up the business code a bit to have to create and pass dependency factories each time I created an object, when it was perfectly fine (aside from testing) for that object to create it's dependencies. I stumbled upon another form of dependency injection that seems to solve both problems for me and I'm wondering if anyone can tell me why it's not a good idea.
Here's a simple, contrived non-realistic example... assuming File and FileFactory are interfaces.
Interface-base constructor DI
public class Logger {
private File file;
public Logger(FileFactory fileFactory, String fileName) {
file = fileFactory.create(fileName);
file.createNewFile();
}
public void debug(String message) {
file.append("[debug] " + message);
}
public void error(String message) {
file.append("[error] " + message);
}
}
Partial-mock dependency injection
public class Logger {
private File file;
public Logger(String fileName) {
file = createFile(fileName);
file.createNewFile();
}
public void debug(String message) {
file.append("[debug] " + message);
}
public void error(String message) {
file.append("[error] " + message);
}
public File createFile(String fileName) {
return new File(fileName);
}
}
With the second example, there are no interfaces or factories and relationships between classes are preserved, so I can ctrl+click to "createNewFile()" or "append". While writing the test I can do a partial mock of this class w/ Mockito and direct it to return a mocked File for "createFile(String)", so I can still inject dependencies during testing. I see advantages to doing it this way. Are there any disadvantages I'm not seeing?
Upvotes: 1
Views: 197
Reputation: 4643
I'm not too much into java but I know DI in C# so I think I can help a bit.
First off, read this stackoverflow question. I've asked what to be tested and why mock should be made. Hopefully, the question can give a general idea of doing unit testing.
For me, what should be done in mock objects:
Simple
Simple mock object can make your test unit easier to review. Simple mock object can make it trivial and not need any testing for the mock object. A simple mock object will support point 4 below.
Have minimum dependency
In your case, you has less dependency to FileFactory. It is good, because it is easier to setup test. Another good thing about having minimum dependency is it has minimum test scope. It also will support the point 4 below.
Has no additional functionality that the interface
In your sample, you has another function public File createFile
. I wonder whether it will be used by consumer or not (it is public). IMHO, it is better to make it private to ensure the unit test will not call the additional method.
Will not break the test class when the test class is changed
The most important point for mock object is when the test class (consumer) is changed, no error will be caused by mock object. Because you want to unit test the test class, then any error should only be caused by the tested class. It will harder to track when the error are caused by the mock object.
Upvotes: 1