A recent article described some problems when dealing with Java interfaces, coming from a language that allows duck typing.
The bottom line is this. There is an interface:
public interface DynamoImpl {
public PutItemResult putItem(PutItemRequest putItemRequest);
public GetItemResult getItem(GetItemRequest getItemRequest);
}
And there is a class AmazonDynamoDB
. That class has the two above methods, but also a lot more. Now John, the programmer, wants to expose only these two methods of AmazonDynamoDB
to the rest of his program. He wants to use them in his class Database
:
public class Database {
public DynamoImpl db;
// code removed for simplicity
public Database(DynamoImpl db) {
this.db = db;
}
}
Now: in a language that supports duck typing, you could just pass an instance of class AmazonDynamoDB
to the constructor, and call the two methods (because the class has them). Not in Java, though. You need to pass in an instance of a class that literally implements
the DynamoImpl
interface (or extends a class that does so). But AmazonDynamoDB
does not implement that interface.
The easiest way to achieve the same goal in Java is to create a wrapper that delegates to an AmazonDynamoDB
instance:
public class AmazonDynamoDbWrapper implements DynamoImpl{
private AmazonDynamoDB amazonDynamoDB;
public AmazonDynamoDbWrapper(AmazonDynamoDB amazonDynamoDB) {
this.amazonDynamoDB = amazonDynamoDB;
}
@Override
public PutItemResult putItem(PutItemRequest putItemRequest) {
return amazonDynamoDB.putItem(putItemRequest);
}
@Override
public GetItemResult getItem(GetItemRequest getItemRequest) {
return amazonDynamoDB.getItem(getItemRequest);
}
}
Yes, a bit verbose, but pretty straight forward once you understand the pattern. Now you can pass an instance of this class to an instance of Database
.
So much for the production code. What about tests?
The easiest solution is to provide a minimal implementation of the DynamoImpl
interface, like so:
public class StubbedDynamoImpl implements DynamoImpl {
@Override
public PutItemResult putItem(PutItemRequest putItemRequest) {
return new PutItemResult(/* Test data here */);
}
@Override
public GetItemResult getItem(GetItemRequest getItemRequest) {
return new GetItemResult(/* Test data here */);
}
}
Again, you can pass an instance of this class to the Database
instance's constructor, in a test:
public class DatabaseTest {
@Test
public void createsDatabase() {
Database database = new Database(new StubbedDynamoImpl());
// Whatever your test assertions are, here
}
// ...
}
No need for fancy mocking frameworks if all you want to expose is two methods.
This is the easiest solution I could think of. I hope it's useful.
P.S.
One word concerning Java conventions: please don't use the suffix Impl for interfaces. This should be used for concrete implementation classes (if at all), or otherwise you might confuse a lot of people.