2015-08-28 37 views
10

Tôi có việc xây dựng đoạn mã sau:Mã bảo hiểm khối finally

try { 
    //some code 
} 
catch(CustomException custExc) { 
    //log 
} 
catch(CustomException2 custExc2) { 
    //log 
} 
catch(Exception exc) { 
    //log 
} 
finally { 
    //some code 
} 

tôi đã viết các bài kiểm tra đơn vị: người đầu tiên đề cập tình hình khi một ngoại lệ không được ném (thực hiện chỉ thử mã khối và khối finally code) và 3 khác là chúng được bao phủ mỗi khối catch cùng một lúc (thực thi khối try, một khối catch và cuối cùng là block). Vấn đề là plugin Eclipse Emma đã cho thấy rằng tôi đã không bao giờ chặn cuối cùng. Bất kỳ ý tưởng tại sao nó có thể xảy ra?

Trả lời

1

Trong khi tôi đang thử nghiệm một số trường hợp, tôi phát hiện ra rằng bạn có thể không có bảo hiểm trường hợp, khi một ngoại lệ không bắt được ném.

Với ví dụ sau:

import java.text.ParseException; 
import java.text.SimpleDateFormat; 

import org.junit.Test; 

public class CodeCoverageFinallyTest { 
    @Test 
    public void testMyMethod() { 
     myMethod("2015-08-31"); 
     myMethod("wrongFormat"); 
    } 

    private void myMethod(final String source) { 
     try { 
      new SimpleDateFormat("yyyy-MM-dd").parse(source); 
     } catch (final ParseException e) { 
      System.out.println("catch ParseException"); 
     } finally { 
      System.out.println("finally"); 
     } 
    } 
} 

Ví dụ này sẽ chỉ bắt một trong hai chi nhánh tại các khối finally bởi vì bạn không kiểm tra các trường hợp, nếu một ngoại lệ được kiểm soát (ví dụ một NullPointerException) sẽ ném.

Vì vậy, nếu bạn thay đổi testcase của bạn một chút, bạn sẽ bắt tất cả các ngành trong khối cuối cùng:

public void testMyMethod() { 
    myMethod("2015-08-31"); 
    myMethod("wrongFormat"); 
    myMethod(null); // also cover the case, that an unchecked and unhandled exception 
         // will be thrown 
} 

Trong testcase khác của tôi, tôi đã có một trường hợp sligthly khác nhau với một số if-else-if xây dựng.

import org.junit.Test; 

public class CodeCoverageIfElseTest { 
    @Test 
    public void testMyMethod() { 
     myMethod("2015-08-31"); 
     myMethod("wrongFormat"); 
    } 

    private void myMethod(final String source) { 
     if ("2015-08-31".equals(source)) { 
      System.out.println("Correct format"); 
     } else if ("wrongFormat".equals(source)) { 
      System.out.println("Incorrect format"); 
     } 
    } 
} 

Ở đây else if không bắt chi nhánh thứ hai bởi vì, những gì nếu điều kiện ifelse if sẽ không được đúng không? Nó cũng sẽ bị bắt nếu bạn cung cấp các giá trị khác so với cả hai trong câu lệnh if-else-if.

5

Trong mã Java bytecode (ít nhất là từ Java 1.6) không có cấu trúc đặc biệt nào cho khối finally, do đó, nó thực sự được sao chép nhiều lần. Ví dụ, hãy xem xét các phương pháp sau đây:

public static void main(String[] args) { 
    try { 
     System.out.println("In try"); 
     if(args.length > 0) 
      return; 
     System.out.println("No args"); 
    } 
    catch(RuntimeException ex) { 
     System.out.println("In catch"); 
    } 
    finally { 
     System.out.println("In finally"); 
    } 
} 

Mã này được hiệu quả biên soạn một cái gì đó như thế này:

public static void main(String[] args) { 
    try { 
     System.out.println("In try"); 
     if(args.length > 0) { 
      System.out.println("In finally"); 
      return; 
     } 
     System.out.println("No args"); 
    } 
    catch(RuntimeException ex) { 
     System.out.println("In catch"); 
     System.out.println("In finally"); 
    } 
    catch(<any exception> t) { 
     System.out.println("In finally"); 
     throw t; 
    } 
    System.out.println("In finally"); 
} 

Đây không phải là một mã hoàn toàn tương đương, bởi vì nếu ngoại lệ mới xảy ra trong System.out.println("In finally"); (trước sự trở lại), sau đó nó sẽ không bị bắt. Tuy nhiên nó cho thấy ý tưởng thô rằng khối finally được nhân đôi ở đây bốn lần. Nó có thể được nhân đôi nhiều lần nếu bạn có một số cách để thoát khỏi khối thử của bạn và đặc biệt là nếu bạn có các khối thử cuối cùng lồng nhau. Cũng lưu ý rằng bắt thêm đặc biệt <any exception>. Nó sẽ xuất hiện trong bytecode ngay cả khi bạn viết một cách rõ ràng catch(Throwable t).

Vì các công cụ bảo vệ mã như Emma hoặc JaCoCo hoạt động ở cấp mã byte, họ không biết rằng bốn bản in "In finally" này thực sự là cùng một tuyên bố trong mã nguồn. Có thể thực hiện phân tích bytecode và xác định rõ phần nào của bytecode tương ứng với nguồn đơn cuối cùng đã chặn (tôi thực sự đã viết phân tích một lần), nhưng nó không phải là vấn đề rất dễ dàng và có một số cảnh báo không tầm thường. Bạn cũng nên tính đến các trình biên dịch khác nhau (ví dụ, javac và ecj) tạo ra cách bố trí hơi khác nhau của các khối cuối cùng. Vì vậy, có vẻ như công việc này đã không được thực hiện trong các công cụ bảo hiểm phổ biến và họ chỉ xem xét khác nhau cuối cùng khối bản sao như mã khác nhau.

Trong trường hợp cụ thể của bạn có vẻ như @bobbel là đúng: bạn không kiểm tra trường hợp ngoại lệ chưa xử lý (mà <any exception> bắt).

+0

"Cũng lưu ý các bắt đặc biệt bổ sung Nó sẽ xuất hiện trong bytecode ngay cả khi bạn viết một cách rõ ràng (Throwable t). " - Tôi tin rằng điều này là sai, tức là 'catch (java/lang/Throwable)' và 'catch (any)' - hai trình xử lý ngoại lệ riêng biệt trong bytecode, ngay cả khi 'Throwable' là một lớp cơ sở của tất cả các lỗi và ngoại lệ. – Godin

0

Screenshot about green coverage

Vâng, chi nhánh mất tích, khi một Throwable còn tự do ném.

Nếu bạn tò mò về chủ đề này, tôi khuyên bạn trang github của tôi, nơi tôi thử tất cả những thứ này ra:. https://github.com/bachoreczm/basicjava

Các vấn đề liên quan