07/09/2018, 18:07

Giới thiệu các công cụ phân tích tĩnh giúp nâng cao chất lượng Code Android (Phần 1)

Đây là những công cụ dùng để phân tích mã nguồn của bạn mà không thật sự thực thi nó. Mục đích là để tìm ra các lỗ hổng tiềm tàng ví dụ như các lỗ hổng và lỗi bảo mật. Một trình phân tích code miễn phí phổ biến chẳng hạn như FindBugs kiểm tra code của bạn dựa trên một bộ quy tắc mà code của bạn nên ...

Đây là những công cụ dùng để phân tích mã nguồn của bạn mà không thật sự thực thi nó. Mục đích là để tìm ra các lỗ hổng tiềm tàng ví dụ như các lỗ hổng và lỗi bảo mật. Một trình phân tích code miễn phí phổ biến chẳng hạn như FindBugs kiểm tra code của bạn dựa trên một bộ quy tắc mà code của bạn nên tuân theo - nếu code không tuân theo các quy tắc này, thì đó là dấu hiệu cho thấy có điều gì đó không đúng. Hãy nghĩ về các công cụ phân tích code tĩnh giống như là một trình biên dịch phụ được chạy trước quá trình biên dịch cuối cùng thành ngôn ngữ hệ thống.

Nhiều công ty phần mềm yêu cầu các dự án phải trải qua các bài kiểm tra phân tích code tĩnh, bên cạnh việc kiểm tra code và kiểm thử trong quá trình build. Ngay cả những người bảo trì các dự án mã nguồn mở cũng thường bao gồm một hoặc nhiều bước phân tích code tĩnh trong quá trình build. Vì vậy, việc học về phân tích code tĩnh là một bước quan trọng trong việc viết code có chất lượng. Xin lưu ý rằng phân tích code tĩnh - còn được gọi là kiểm thử "white-box" - không nên được xem là một sự thay thế cho việc kiểm thử mã nguồn của bạn.

Trong hướng dẫn này, chúng ta sẽ tìm hiểu về một số công cụ phân tích code tĩnh phổ biến, có sẵn cho Android và Java. Nhưng trước tiên, hãy cùng xem một số lợi ích của việc sử dụng các công cụ phân tích code tĩnh.

Lợi ích

Giúp phát hiện các lỗi tiềm tàng mà ngay cả việc kiểm thử hoặc kiểm tra thủ công đều có thể bỏ lỡ.
Đặt ra các quy tắc cụ thể của dự án. Ví dụ, các công cụ phân tích code tĩnh là một phần của chuỗi build giúp những người mới bắt kịp dự án với các tiêu chuẩn code của nhóm mới.
Giúp bạn nâng cao kiến thức về một ngôn ngữ mới.
Quét toàn bộ dự án của bạn, bao gồm các tập tin mà bạn có thể chưa từng đọc.

Tất cả các công cụ phân tích code mà chúng ta sẽ tìm hiểu trong hướng dẫn này tồn tại dưới dạng các plugin của Gradle, vì vậy chúng ta có thể tạo ra các tác vụ Gradle riêng lẻ cho từng công cụ. Hãy sử dụng một tập tin Gradle duy nhất sẽ bao gồm tất cả chúng. Nhưng trước khi làm điều đó, hãy tạo một thư mục chứa tất cả các tập tin của chúng ta dùng để phân tích code tĩnh.

Hãy mở Android Studio và bên trong mô-đun app (trong chế độ Project), tạo một thư mục mới và đặt tên cho nó là code_quality_tools. Thư mục này sẽ chứa các tập tin XML cho các công cụ phân tích code, và nó cũng sẽ có một tập tin Gradle, quality.gradle sẽ chạy các tác vụ phân tích code tĩnh của chúng ta.

Sau cùng, hãy truy cập vào tập tin build.gradle của bạn trong thư mục app và bao gồm dòng này ở cuối tập tin:

apply from: '/code_quality_tools/quality.gradle'

Ở đây, script Gradle quality.gradle của chúng ta đang được áp dụng một tham chiếu đến vị trí tập tin cục bộ của nó.

Checkstyle

Giả sử các quy tắc mà bạn chỉ định nằm trong một tập tin XML để tuân theo một tiêu chuẩn viết code cho dự án của bạn, thì Checkstyle sẽ áp dụng các quy tắc này bằng cách phân tích mã nguồn của bạn và so sánh chúng với các tiêu chuẩn hoặc quy ước viết code đã biết.

Checkstyle là một công cụ mã nguồn mở được duy trì một cách tích cực bởi cộng đồng. Điều này có nghĩa là bạn có thể tạo ra các kiểm tra tùy biến của riêng bạn hoặc sửa đổi những cái hiện có để phù hợp với nhu cầu của bạn. Ví dụ, Checkstyle có thể chạy một bài kiểm tra trên các tên hằng (final, static, hoặc cả hai) trong các lớp của bạn. Nếu các tên hằng của bạn không tuân theo quy tắc là chữ hoa với các từ được phân cách bởi một dấu gạch dưới, thì vấn đề sẽ được gắn cờ trong báo cáo cuối cùng.

// incorrect 
private final static String myConstant = "myConstant";
 
// correct
private final static String MY_CONSTANT = "myConstant";

Tích hợp Checkstyle

Tôi sẽ hướng dẫn cho bạn cách tích hợp Checkstyle vào dự án Android Studio của chúng ta và minh họa một ví dụ thực tế.

Trước tiên, chúng ta cần phải tạo ra các quy tắc viết code của chúng ta. Bên trong checkstyle.xml, chúng ta tạo ra một số quy tắc cấu hình Checkstyle mà sẽ được chạy trên code của chúng ta.

<?xml version="1.0"?><!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<module name="Checker">
    <module name="FileTabCharacter"/>
    <module name="TreeWalker">
 
         
         
        <module name="MethodName"/>
        <module name="ConstantName"/>
 
         
         
        <module name="AvoidStarImport"/>
        <module name="UnusedImports"/>
 
         
         
        <module name="ParameterNumber">
            <property name="max" value="6"/>
        </module>
         
         
</module>

Trong đoạn code ở trên, chúng ta bao gồm các quy tắc hoặc kiểm tra mà chúng ta muốn Checkstyle kiểm tra trong mã nguồn của chúng ta. Một quy tắc đó là AvoidStarImport, như tên gọi của nó, kiểm tra xem mã nguồn của bạn có bao gồm một câu lệnh import giống như java.util.* hay không. (Thay vì như vậy, bạn nên chỉ định rõ ràng gói cần import, ví dụ như, java.util.Observable.)

Một số quy tắc có các thuộc tính mà chúng ta có thể thiết lập giống như chúng ta đã làm cho ParameterNumber - thuộc tính này giới hạn số lượng các tham số của một phương thức hoặc hàm xây dựng. Mặc định, thuộc tính max là 7, nhưng chúng ta đã thay đổi nó thành 6. Hãy tham khảo một số kiểm tra khác trên trang web của Checkstyle.

Để chạy bài kiểm tra này, chúng ta cần tạo một tác vụ Gradle. Vì vậy, hãy truy cập vào tập tin quality.gradle và tạo một tác vụ gọi là checkstyle:

apply plugin: 'checkstyle'
 
task checkstyle(type: Checkstyle) {
    description 'Check code standard'
    group 'verification'
 
    configFile file('./code_quality_tools/checkstyle.xml')
    source 'src'
    include '**/*.java'
    exclude '**/gen/**'
 
    classpath = files()
    ignoreFailures = false
}

Lưu ý rằng trong đoạn code ở trên, trước tiên chúng ta áp dụng plugin Checkstyle Gradle. Chúng ta gán cho nó một mô tả và thêm nó vào một nhóm Gradle đã xác định trước đó được gọi là verification.

Các thuộc tính chính của tác vụ Checkstyle Gradle mà chúng ta quan tâm là:

configFile: tập tin tin cấu hình Checkstyle cần sử dụng.
IgnoreFailures: cho phép hay không tiếp tục quá trình build nếu có cảnh báo.
include: bộ các mẫu sẽ bao gồm.
exclude: tập hợp các mẫu không bao gồm. Trong trường hợp này, chúng ta không quét các lớp được tạo ra.
Cuối cùng, bạn có thể chạy script Gradle bằng cách truy cập vào cửa sổ công cụ Gradle trên Android Studio, mở nhóm verification và sau đó nhấp vào checkstyle để chạy tác vụ.

Một cách khác là sử dụng dòng lệnh:
gradle checkstyle
Sau khi tác vụ đã hoàn tất, một báo cáo sẽ được tạo ra, nằm tại mô-đun app > build > reports > checkstyle. Bạn có thể mở checkstyle.html để xem báo cáo.

Plugin Checkstyle miễn phí cho Android Studio hoặc IntelliJ IDEA. Nó cung cấp khả năng quét theo thời gian thực các tập tin Java của bạn.

PMD

PMD là một công cụ mã nguồn mở khác dùng để phân tích mã nguồn của bạn. Nó tìm kiếm những sai sót phổ biến ví dụ như các biến không sử dụng, các khối catch rỗng, việc tạo ra các đối tượng không cần thiết và vân vân. PMD có nhiều bộ quy tắc mà bạn có thể lựa chọn. Ví dụ về một quy tắc là một phần của bộ Quy tắc Thiết kế là:

SimplifyBooleanExpressions: hạn chế những so sánh không cần thiết trong các biểu thức boolean mà làm phức tạp hoá code. Ví dụ:

public class Bar {
 // can be simplified to
 // bar = isFoo();
 private boolean bar = (isFoo() == true);
 
 public isFoo() { return false;}
}

PMD được cấu hình bằng tập tin pmd.xml. Bên trong nó, chúng ta sẽ bao gồm một số quy tắc cấu hình chẳng hạn như các quy tắc dành cho Android, Đặt tên và Thiết kế.

<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Android Application Rules"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd">
 
    <description>Custom ruleset for Android application</description>
 
    <exclude-pattern>.*/R.java</exclude-pattern>
    <exclude-pattern>.*/gen/.*</exclude-pattern>
 
     
     
    <rule ref="rulesets/java/android.xml"/>
     
     
     
    <rule ref="rulesets/java/design.xml">
        <exclude name="UncommentedEmptyMethod"/>
    </rule>
 
     
     
    <rule ref="rulesets/java/naming.xml/ShortClassName">
        <properties>
            <property name="minimum" value="3"/>
        </properties>
    </rule>
     
</ruleset>

Như chúng ta đã làm với Checkstyle, chúng ta cũng cần phải tạo một tác vụ PMD Gradle để bài kiểm tra được thực thi bên trong tập tin quality.gradle.

apply plugin: 'pmd'
 
task pmd(type: Pmd) {
    description 'Run PMD'
    group 'verification'
    ruleSetFiles = files("./code_quality_tools/pmd.xml")
    source 'src'
    include '**/*.java'
    exclude '**/gen/**'
    reports {
        xml.enabled = false
        html.enabled = true
    }
 
    ignoreFailures = false
}

PMD cũng sẵn có dưới dạng một plugin Gradle.

Các thuộc tính chính của tác vụ mà chúng ta tạo ra là:

  • ruleSetFiles: Các tập tin quy tắc tùy biến được sử dụng.
  • source: Nguồn cho tác vụ này.
  • reports: Các báo cáo được tạo ra bởi tác vụ này.
    Cuối cùng, bạn có thể chạy script Gradle bằng cách truy cập vào cửa sổ công cụ Gradle, mở thư mục nhóm verification, sau đó nhấp vào pmd để chạy tác vụ. Hoặc bạn có thể chạy nó thông qua dòng lệnh:
gradle pmd

Một báo cáo cũng sẽ được tạo ra sau quá trình thực thi của tác vụ nằm trong mô-đun app > build> reports > pmd. Đồng thời có một plugin PMD dành cho IntelliJ hoặc Android Studio để bạn tải xuống và tích hợp nếu muốn.

FindBugs

FindBugs là một công cụ miễn phí khác dùng để phân tích lớp nhằm tìm ra các vấn đề tiềm tàng bằng cách kiểm tra các bytecode của bạn dựa vào một danh sách các mẫu lỗi đã biết. Một trong số đó là:

Class defines hashCode() but not equals(): Một lớp cài đặt phương thức hashCode() nhưng không equals() - do đó hai đối tượng có thể bằng nhau nhưng không có cùng mã băm. Điều này rơi vào trường hợp cách làm không tốt.
Bad comparison of int value with long constant: Code so sánh một giá trị int với một hằng số dài nằm ngoài phạm vi các giá trị có thể được biểu diễn dưới dạng một giá trị int. Việc so sánh này là vô nghĩa và có thể sẽ xuất ra một kết quả bất ngờ. Việc này rơi vào nhóm tính chính xác.
TestCase has no tests: lớp là một JUnit TestCase nhưng chưa cài đặt bất kỳ phương thức kiểm thử nào. Kiểu này cũng thuộc loại tính chính xác.
FindBugs là một dự án mã nguồn mở, do đó bạn có thể xem, đóng góp hoặc theo dõi tiến độ của mã nguồn trên GitHub.

Trong tập tin findbugs-exclude.xml, chúng ta muốn ngăn FindBugs quét một số lớp (bằng các biểu thức chính quy) trong các dự án của chúng ta, chẳng hạn như các lớp tài nguyên và các lớp manifest tự động được tạo ra. Ngoài ra, nếu bạn sử dụng Dagger, bạn sẽ muốn FindBugs không kiểm tra các lớp Dagger tự động tạo ra. Chúng ta cũng có thể nói cho FindBugs bỏ qua một số quy tắc nếu muốn.

<FindBugsFilter>
     
    <Match>
        <Class name="~.*R$.*"/>
    </Match>
 
     
    <Match>
        <Class name="~.*Manifest$.*"/>
    </Match>
 
     
    <Match>
        <Class name="~.*Dagger*.*"/>
    </Match>
     
     
     <Match>
        <Bug pattern="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD" />
    </Match>
</FindBugsFilter>

Và cuối cùng, chúng ta sẽ bao gồm tác vụ findbugs trong quality.gradle:

apply plugin: 'findbugs'
 
task findbugs(type: FindBugs) {
    description 'Run findbugs'
    group 'verification'
    classes = files("$project.buildDir/intermediates/classes")
    source 'src'
    classpath = files()
    effort 'max'
    reportLevel = "high"
    excludeFilter file('./code_quality_tools/findbugs-exclude.xml')
    reports {
        xml.enabled = false
        html.enabled = true
    }
    ignoreFailures = false
}

Ở dòng đầu tiên, chúng ta áp dụng FindBugs như là một Plugin Gradle và sau đó tạo ra một tác vụ được gọi là findbugs. Các thuộc tính chính của tác vụ findbugs mà chúng ta thật sự quan tâm là:

  • classes: các lớp cần được phân tích.
  • effort: mức độ phân tích. Giá trị được chỉ định phải là một trong min, default hoặc max. Lưu ý rằng các cấp độ cao hơn sẽ tăng độ chính xác và tìm ra nhiều lỗi hơn với chi phí chạy và tiêu tốn bộ nhớ hơn.
  • reportLevel: ngưỡng ưu tiên cho việc báo cáo lỗi. Nếu thiết lập là low, thì tất cả các lỗi đều được báo cáo. Nếu được thiết lập là medium (mặc định) thì các lỗi ưu tiên trung bình và cao sẽ được báo cáo. Nếu được thiết lập thành high, thì chỉ báo cáo các lỗi có mức độ ưu tiên cao.
  • excludeFilter: tên tập tin của một bộ lọc xác định các lỗi để loại trừ khỏi báo cáo mà chúng ta đã tạo ra.
    Sau đó, bạn có thể chạy script Gradle bằng cách truy cập vào cửa sổ công cụ Gradle, mở thư mục nhóm verification và sau đó nhấp vào findbugs để chạy tác vụ. Hoặc khởi động nó từ dòng lệnh:
gradle findbugs

Một báo cáo cũng sẽ được tạo ra khi quá trình thực thi của tác vụ đã hoàn tất. Báo cáo này sẽ nằm tại mô-đun app > build > reports > findbugs. Plugin FindBugs là một plugin miễn phí khác sẵn có để tải xuống và tích hợp vào IntelliJ IDEA hoặc Android Studio.

Nguồn tham khảo

https://code.tutsplus.com/tutorials/ensure-high-quality-android-code-with-static-analysis-tools--cms-28787

0