Testing an Object's Internal State with PowerMock
Most unit testing focuses on testing an object’s behaviour in order to
prove that it works. This is achieved by writing a JUnit test that calls
an object’s public methods and then testing that the return values from
these calls match some previously defined set of expected values. This
is a very common and successful technique; however, it shouldn't be
forgotten that objects also exhibit state; something that is, by virtue
of the fact that it’s hidden, often overlooked.
Grady Booch’s 1994 Book Object Oriented Analysis and Design, which I first read in the summer of 1995 defines an object’s state in the following way:
He defines the difference between static state and dynamic state using a vending machine example. Static state is exhibited by the way that the machine is always ready to take your money, whilst dynamic state is how much of your money it’s got at any given instance.
I suspect that at this point, you’ll quite rightly argue that explicit behavioural tests do test an object’s state by virtue of the fact that a given method call returned the correct result and that to get the correct result the object’s state had to also be correct... and I’ll agree. There are, however, those very few cases where classic behavioural testing isn’t applicable. This occurs when a public method call has no output and does nothing to an object except change its state. An example of this would be a method that returned void or a constructor. For example, given a method with the following signature:
public void init();
…how do you ensure it’s done its job? It turns out that there are several methods you can use to achieve this...
- Add lots of getter methods to your class. This is not a particularly good idea, as you’re simply loosening encapsulation by the back door.
- Relax encapsulation: make private instance variables package private. A very debatable thing to do. You could pragmatically argue that having well tested, correct and reliable code may be better than having a high degree of encapsulation, but I’m not too sure here. This may be a short term fix, but could lead to all kinds of problems in the future and there should be a way of writing well tested, correct and reliable code that doesn’t include breaking an object’s encapsulation
- Write some code that uses reflection to access an object’s internal state. This is the best idea to date. The down side is that it’s a fair amount of effort and requires a reasonable amount of programming competence.
- Use PowerMock’s Whitebox testing class to do the hard work for you.
public class AnchorTag {
private static final Logger logger = LoggerFactory.getLogger(AnchorTag.class);
/** Use the regex to figure out if the argument is a URL */
private final Pattern pattern = Pattern.compile("^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}$");
/**
* A public method that uses the private method
*/
public String getTag(String url, String description) {
validate(url, description);
String anchor = createNewTag(url, description);
logger.info("This is the new tag: " + anchor);
return "The tag is okay";
}
/**
* A private method that's used internally, but is complex enough to require testing in its own right
*/
private void validate(String url, String description) {
Matcher m = pattern.matcher(url);
if (!m.matches()) {
throw new IllegalArgumentException();
}
}
private String createNewTag(String url, String description) {
return "<a href=\"" + url + "\">" + description + "</a>";
}
}The URL validation test is done using a regular expression and a Java Pattern object. Using the Whitebox class will ensure that the pattern object is configured correctly and that our AnchorTag is in the correct state. This demonstrated by the JUnit test below:
/**
* Works for private instance vars. Does not work for static vars.
*/
@Test
public void accessPrivateInstanceVarTest() throws Exception {
Pattern result = Whitebox.<Pattern> getInternalState(instance, "pattern");
logger.info("Broke encapsulation to get hold of state: " + result.pattern());
assertEquals("^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}$", result.pattern());
}The crux of this test is the line:
Pattern result = Whitebox.<Pattern> getInternalState(instance, "pattern");
...which uses reflection to return the Pattern object private instance variable. Once we have access to this object, we simply ask it if it has been initialised correctly be calling:
assertEquals("^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}$", result.pattern());In conclusion I would suggest that using PowerMock to explicitly test an object’s internal state should be used only when you can’t use straight forward classic JUnit test for behavioural testing. Having said that, it is another tool in your toolbox that’ll help you to write better code.
From http://www.captaindebug.com/2011/10/testing-objects-internal-state-with.html
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)






Comments
Marc-Daniel Ortega replied on Thu, 2011/10/27 - 12:34am
Rob Jones replied on Thu, 2011/10/27 - 7:55am
This would not return the object, but just a part of the objects state. But all this would have been for the pure benefit of unit testing and (as you mentioned) exposed the value of the inner state, which I may not want/need to do.public String getPatternString() {return pattern.pattern();
}
Witold Szczerba replied on Fri, 2011/10/28 - 9:45am
Unit-tests (and generally tests) are NOT supposed to test object's internal state or implementation. Tests are supposed to verify how objects are playing their roles by examining messages sent and received. This is what mocks are for.
Tests which are asserting on internal state of objects are waste of time. They will go in you way everytime you will have to refactor and refactoring is driven by the endless need of changes. Making refactoring harder slows down development and the overall shape of the application goes down to the point where no one can actually understand it, no one dares to touch anything, etc., etc... we all know it.
I would suggest spending time on discovering how to solve the problem the OOP-way ("tell, don't ask" rule), so there would be no need for dirty hacks instead.
Few days ago, Giorgio Sironi wrote about "tell don't ask" principle here:
http://java.dzone.com/articles/tell-dont-ask-case-web-service
He has also provided a link to a very nice presentation:
http://confreaks.net/videos/659-rubyconf2011-why-you-don-t-get-mock-objects
If you do not want to invest 45 minutes of your time to watch this (I strongly recommend to watch the whole) than at least fast-forward to 19m26sec. First rule of mocking: mock roles, not objects, expect correct behaviour instead of asserting on internal state. All the details are here:
http://www.jmock.org/oopsla2004.pdf
P.S.
Mocks are not stubs! :) :)
Attila Magyar replied on Fri, 2011/10/28 - 9:54am
in response to:
Witold Szczerba
Roger Hughes replied on Fri, 2011/10/28 - 10:40am
Robert Craft replied on Thu, 2012/01/26 - 5:55am
Spring Security