The fundamental theorem of software engineering (FTSE) is a term originated by Andrew Koenig to describe a remark by Butler Lampson attributed to the late David J. Wheeler:
"We can solve any problem by introducing an extra level of indirection."
[...]
The theorem is often expanded by the humorous clause "…except for the problem of too many levels of indirection," referring to the fact that too many abstractions may create intrinsic complexity issues of their own. (Source: Wikipedia)
So let's say there's a class that has a static method named encode
:
public final class UrlHelper {
protected static String encode(String url) {
try {
url = URLEncoder.encode(url, StandardCharsets.UTF_8.toString());
} catch (Exception e) {
LOGGER.warn("exception occured while encoding url {}", url);
}
return url;
}
}
and your code depends on it:
public class MyClass {
public void doSomething(String someUrl) {
// ...
String encodedUrl = UrlHelper.encode(someUrl);
// ...
}
}
and you want to test MyClass.doSomething(String someUrl)
but you want to mock UrlHelper.encode(someUrl)
. One option is to define another class such as
public final class MyUrlHelper {
protected String encode(String url) {
return UrlHelper.encode(someUrl);
}
}
As MyUrlHelper.encode(String url)
is not static, you can refactor your original code and test it by relying on dependency injection and mocking the non-static MyUrlHelper.encode(String url)
:
// Refactored
public class MyClass {
private MyUrlHelper myUrlHelper;
public UrlHelper(MyUrlHelper muUrlHelper) {
this.myUrlHelper = myUrlHelper;
}
public void doSomething(String someUrl) {
// ...
String encodedUrl = myUrlHelper.encode(someUrl);
// ...
}
}
// Test
@Test
public void myTest() {
// setup myUrlHelper and configure it
MyUrlHelper myUrlHelper = mock(MyUrlHelper.class);
when(myUrlHelper.encode(...)).thenReturn(...);
// inject
MyClass myObject = new MyClass(myUrlHelper);
// stimulate
myObject.doSomething("...")
}
Another option is to use Mockito using the PowerMockRunner as explained by @Marimuthu Madasamy.
However, I don't see any benefit in mocking UrlHelper.encode
or URLEncoder.encode
here. It is not an external system (a database, a file system, a message broker, a SOAP API, a REST API, etc.) so I don't see any gains by mocking it.