Unit testing tricks: Look ma, no setters!
Here’s a neat trick if you want set an object in a specific state in a unit test, but you don’t want to violate encapsulation:
@Test
public void withdrawShouldReduceBalance() {
Account account = new Account() {{
super.balance = 100;
}};
account.withdraw(10);
assertEquals(90, account.getBalance());
}
@Test(expected=IllegalStateException.class)
public void overdraftShouldThrow() {
Account account = new Account() {{
super.balance = 5;
}};
account.withdraw(10);
}This seemingly magic code lets me have a protected (but sadly not private) field Account#balance. I can set this field in each individual test method.
It looks very magic, so it deserves a brief explanation:
- The first set of curlies create an anonymous subclass of Account. That is:
new Account() {}. - The second set of curlies creates an initializer block, that is: a block of code that is added to the constructor.
- So in effect: Each test method creates a subclass of Account and modifies the account field in the constructor. This is why
Account#balancemust be protected instead of private.
This trick (and it is a trick) lets me set up the object under test to any state I want, without needing a special constructor and without breaking encapsulation by adding a setter. However, I’m required to make the fields I want to initialize protected. Additionally, each test method creates one or more new classes. This could potentially affect performance and/or memory use during compilation and/or running of tests. I’ve only used the trick at a small scale, so I don’t know if I’ll run into these issues in the future.
So, what do you think: How big of a “WTF” is this code?
This work is licensed under a
Creative Commons Attribution 3.0 License.
Print This Post