|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 5
Even if you always create simple constructors, most likely you'll remain exposed to classes that lack simple constructors
and need mock objects. For example, let's say you need to create a mock object for the following class, A (which most likely was not developed following the TDD approach):
class A {
public A() throws IOException {
this.thread = new Thread();
this.fileWriter = new FileWriter(new File("out.txt"));
}
}
Note how this class can easily be restructured into the following two classes:
public class A {
public A(Thread thread, FileWriter fileWriter) {
this.thread = thread;
this.fileWriter = fileWriter;
}
}
public class AFactory {
public A getA() throws IOException {
Thread thread = new Thread();
FileWriter fileWriter = new FileWriter(new File("out.txt"));
return new A(thread, fileWriter);
}
}
Now it is trivial to create a mock object for class A, because class A's constructor does nothing substantive with its input parameters. Your mock object can simply pass in null for these parameters and override A's other methods to perform the behavior it wants. Not only is it simpler to mock up object A, it's also simpler to test A. The unit tests for A can now focus primarily on A's responsibilities (i.e., its methods), leaving the tests related to constructing A in AFactory's unit tests.
| Note |
|---|
Although you are more likely to see a class like the original class A when dealing with legacy code, in our experience, it is possible to create such a class even while following a TDD approach.
The problem with class A may not become apparent until you need to create a mock object for A. However, once you are familiar with this issue, you are unlikely to create complicated constructors in future use of TDD.
|
Remember that we created this factory to simplify mock object creation for unit tests. Using factories to abstract away the complexities of creation into a separate class is a common best practice of object-oriented programming. This leads us to an interesting observation: A common discovery when doing TDD is that when you produce code easier to test, you've also produced higher-quality code. TDD steers you towards using proper object-oriented methodologies.
One final note on this topic: We do sometimes create concrete production classes that throw RuntimeExceptions if required constructor parameters are not provided (e.g., if a constructor argument is null, an IllegalArgumentException might be thrown). Assuming no parent interface for this concrete class exists (and assuming it doesn't make sense to extract
such an interface), your mock object is stuck overriding a concrete class whose constructor throws exceptions (albeit RuntimeExceptions, which aren't part of the method's signature). In the next section, we discuss how to create mock objects for such a class.
Since mock objects are so essential to test-driven development, you'd like their development to be as simple as possible. This can be tricky when you need to create many mock objects for a large interface or class, as can often happen when adding unit tests around legacy code (legacy, in this sense, means code not developed with testing in mind—in our experience, interfaces/classes developed with testing in mind tend to be smaller and more focused).
Archived Discussions (Read only)