Because the mocks will be almost exactly the same, you want a way to create both of them without introducing duplicate code. You want to be able to phrase the new test as follows:[4]
public void testFailedTransferFromBank() { account.setAch(createMockAch(AchStatus.FAILURE)); final BigDecimal amount = new BigDecimal("50.00"); account.transferFromBank(amount); assertEquals(new BigDecimal("0.00"), account.getBalance()); } The existing test, testTransferFromBank, can use the same construct. public void testTransferFromBank() { account.setAch(createMockAch(AchStatus.SUCCESS)); final BigDecimal amount = new BigDecimal("50.00"); account.transferFromBank(amount); assertEquals(amount, account.getBalance()); } Anonymous inner class objects are just objects. They can be created in a separate method and returned like any other object. // THIS WON'T COMPILE! private Ach createMockAch(AchStatus status) { return new MockAch() { public AchResponse issueDebit( AchCredentials credentials, AchTransactionData data) { Assert.assertTrue( data.account.equals(AccountTest.ACCOUNT_NUMBER)); Assert.assertTrue(data.aba.equals(AccountTest.ABA)); AchResponse response = new AchResponse(); response.timestamp = new Date(); response.traceCode = "1"; response.status = status; return response; } }; }
However, this code won't compile. You will see the message: local variable status is accessed from within inner class; needs to be declared final Anonymous inner classes are inner classes. By the definition you learned in Lesson 11, an inner classes has access to the instance variables (and methods) of the enclosing class. An anonymous inner class does not have access to local variables. The status parameter is analogous to a local variable because it is accessible only to code in the createMockAch method. According to the compiler error, however, you can get around this limitation by declaring the status parameter as final. private Ach createMockAch(final AchStatus status) After you do this, your code should compile but your tests will not pass. Why must you define the variable as final? Remember that by declaring a variable as final, you are declaring that its value cannot be changed after it has been set. An instance of an anonymous inner class can exist outside of the method in which it is declared. In our example, the createMockAch method instantiates a new anonymous inner class and returns it from the method it will be used by the calling test methods Local variables do not exist once a method has finished executing! Nor do parametersall parameters to methods are copies of the arguments passed by the calling code.[5] If an anonymous inner class was able to access local variables, the value of the local variable could be destroyed by the time the code in the anonymous inner class actually executed. This would, of course, be a bad thing.
To get your tests to pass, insert the code in Account so that you credit only successful transfers. public void transferFromBank(BigDecimal amount) { AchCredentials credentials = createCredentials(); AchTransactionData data = createData(amount); Ach ach = getAch(); AchResponse achResponse = ach.issueDebit(credentials, data); if (achResponse.status == AchStatus.SUCCESS) credit(amount); } With a bit of refactoring, this method simplifies to: public void transferFromBank(BigDecimal amount) { AchResponse achResponse = getAch().issueDebit(createCredentials(), createData(amount)); if (achResponse.status == AchStatus.SUCCESS) credit(amount); } |