Accessing Variables from the Enclosing Class


You need an additional test, testFailedTransferFromBank, to define what happens when the bank rejects the debit request. The test and mock are going to be very similar to testTransferFromBank. The student's balance should not change on a failed transfer, so the test assertion must change. The status returned from the mock in the AchResponseData object must be AchStatus.FAILURE.


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]

[4] You may also want the Account code to throw an exception if the ACH debit fails. I avoided the exception here for simplicity reasons.

 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;       }    }; } 

Class Names for Anonymous Inner Classes

When it compiles a class containing an anonymous inner class, Java uses a similar rule for naming the compilation unit as its rule for anonymous nested classes that are not anonymous. Java uses the dollar sign ($) to separate the class names.

A slight problem: An anonymous inner class has no name! Java provides a simple solution, numbering anonymous inner classes using a counter starting at 1. It then uses this number as the nested inner class name portion of the compilation unit name.

For example, the AccountTest class creates two compilation units: AccountTest.class and AccountTest$1.class.

I'll repeat my warning from last chapter: When you copy class files or put them in a JAR file for distribution, don't forget to include all the nested classes.


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.

[5] For a further discussion, see Additional Lesson III, which talks about call by value.

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); } 



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net