class Extension extends Base
{
void extensionMethod()
{
this.abstractBehaviour(someParameter);
}
}
I want to test interactions, so normally I'd use a mock, but I can't mock out the superclass. A brute force approach is to roll my own mocking by creating a new subclass for testing.
class TestExtension extends Extension
{
boolean wasInvoked = false;
void abstractBehaviour(Object parameter)
{
wasInvoked = true; // could be more complicated
}
}
public void testExtension()
{
TestExtension extension = new TestExtension();
extension.invokeFrameworkMethod();
assertTrue(extension.wasInvoked);
}
An alternative is to inject an object that encapsulates the behaviour as a strategy, like this:
class Extension extends Base
{
Extension(Implementation implementation)
{
this.implementation = implementation;
}
void extensionMethod()
{
this.implementation(this);
}
}
Then I can inject a mock or stub implementation and verify that it is invoked, and separately verify that the implementation does what I expect by injecting a mock/stub Base into the implementation. That certainly separates the two concerns, and each concern is neatly unit tested. It's also quite a bit of code. It also points us in a new direction for the framework - injection of strategy objects for the customisable pieces of code instead of using template method and inheritance.
Ruby can handle the problem using both these approaches, but there's a third alternative that isn't available in Java. In Ruby, I can directly change the behavious of the framework class.
class Base
def extensionMethod
abstractBehaviour(parameters)
end
end
All done. No new classes, no injection, and the entire application is aware of the change once the code is loaded. But how do you test it? We go one step further, and override the behaviour of just the Base instance that we are testing, like this:
specify "Should invoke abstract behaviour" do
base = Base.new
class << base
attr_reader :was_invoked
def abstractBehaviour(parameter)
was_invoked = true # could be more complicated
end
end
base.invokeFrameworkMethod
base.was_invoked.should == true
end
Of course, in Ruby you might still benefit from the flexibility of using injected strategy objects rather than a template method, but you might also be able to get away with something that is even easier than either of the Java alternatives.
No comments:
Post a Comment