[Legacy] Code coverage trong Xcode project

Posted on May 19th, 2019

Coverage là một thông số khá quan trọng trong đảm bảo chất lượng phần mềm, cho phép đánh giá nhanh tính hiệu quả của các task kiểm thử liên quan. Trong quyển sách Foundation Of Software Testing, ISTQB có đoạn nói về coverage như sau:

When coverage is discussed by programmers, it most likely refers to the coverage of code, where the structural elements can be identified using a tool, since there is good tool support for measuring code coverage.

Thật vậy, trong bài này chúng ta sẽ đi qua cách làm thế nào collect code coverage trong các Xcode project. Và sau khi có được code coverage thì dùng tool nào để view thông tin này đâu đó bên ngoài Xcode.

Trước khi bắt đầu cũng nên biết là code coverage chủ yếu xuất hiện trong unit (component) testing và integration testing. Cụ thể, ISTQB cho rằng:

Code coverage is normally done in component and component integration testing - if it is done at all. If someone claims to have achieved code coverage, it is important to establish exactly what elements of the code have been covered.

Configuring Xcode to collect code coverage

Đầu tiên cần enable một thiết lập nhỏ trong project để Xcode có thể thu thập code coverage sau mỗi lần run test. Thực hiện khá đơn giản theo steps sau:

  • Vào menu Product > Scheme > Edit Scheme...
  • Chọn action Test > tab Options
  • Check vào checkbox Code coverage

    • Mục Gather code coverage for mặc định đang chọn all targets. Tuy nhiên có thể được chọn lại cho những target cụ thể bằng cách some targets và add + bên dưới.

Xcode code coverage collection

Với config trên, mỗi sau khi bộ test được run, Xcode sẽ report thông tin code coverage một cách tổng quát cho project và cụ thể cho từng file source code trong Show the Report navigator.

Xcode code coverage report

Tips: có thể click vào từng file source code để xem cụ thể block code nào đã được chạy qua, block nào chưa với màu green hoặc red tương ứng.

Tuy nhiên hiện tại chúng ta chỉ có thể xem code coverage vừa collect được này bên trong Xcode. Để có thể dễ dàng hơn với một giả định sử dụng tool automate fastlane và CI trong tương lai, tốt hơn hết là nên có cách nào đó để xem thông tin bên ngoài Xcode. Phần còn lại sẽ giới thiệu không chỉ một mà là 2 tool hỗ trợ việc này.

Slather

Slather là một command line tool sử dụng khá đơn giản, giúp lấy code coverage report từ Xcode để hiển thị ra console hoặc HTML file. Đồng thời nó cũng cho phép tích hợp với CI và một số cloud service khác như Codecov, Coveralls, Cobertura...

Cài đặt

Slather có thể được cài đặt trên macOS bằng Gem với command: sudo gem install slather

Sử dụng

Do slather chỉ có thể dùng code coverage report từ Xcode nên để có input cho slather làm việc, trước hết cần run test trong Xcode (hoặc từ command line với Xcodebuild). Sau đó, trong terminal di chuyển vào thư mục project, và run command theo pattern: slather coverage -s --scheme YourXcodeSchemeName path/to/project.xcodeproj

$ slather coverage -s --scheme XCCov-Demo XCCov-Demo.xcodeproj
Slathering...
XCCov-Demo/AppDelegate.swift: 7 of 21 lines (33.33%)
XCCov-Demo/DataViewController.swift: 8 of 12 lines (66.67%)
XCCov-Demo/ModelController.swift: 16 of 43 lines (37.21%)
XCCov-Demo/RootViewController.swift: 42 of 64 lines (65.62%)
Tested 73/140 statements
Test Coverage: 52.14%
Slathered

Option -s (--simple-output) trong command trên chỉ định Slather output ra ngay console với format đơn giản, tham khảo một số option khác như sau:

$ slather coverage --help
Usage:
    slather coverage [OPTIONS] [PROJECT]

Parameters:
    [PROJECT]                     Path to the xcodeproj

Options:
    --travis, -t                  Indicate that the builds are running on Travis CI
    --travispro                   Indicate that the builds are running on Travis Pro CI
    --circleci                    Indicate that the builds are running on CircleCI
    --jenkins                     Indicate that the builds are running on Jenkins
    --buildkite                   Indicate that the builds are running on Buildkite
    --teamcity                    Indicate that the builds are running on TeamCity
    --coveralls, -c               Post coverage results to coveralls
    --simple-output, -s           Output coverage results to the terminal
    --gutter-json, -g             Output coverage results as Gutter JSON format
    --cobertura-xml, -x           Output coverage results as Cobertura XML format
    --llvm-cov, -r                Output coverage as llvm-cov format
    --json                        Output coverage results as simple JSON
    --html                        Output coverage results as static html pages
    --show                        Indicate that the static html pages will open automatically
    --build-directory, -b BUILD_DIRECTORY The directory where gcno files will be written to. Defaults to derived data.
    --source-directory SOURCE_DIRECTORY The directory where your source files are located.
    --output-directory OUTPUT_DIRECTORY The directory where your Cobertura XML report will be written to.
    --ignore, -i IGNORE           ignore files conforming to a path
    --verbose, -v                 Enable verbose mode
    --input-format INPUT_FORMAT   Input format (gcov, profdata)
    --scheme SCHEME               The scheme for which the coverage was generated
    --configuration CONFIGURATION The configuration for test that the project was set
    --workspace WORKSPACE         The workspace that the project was built in
    --binary-file BINARY_FILE     The binary file against the which the coverage will be run
    --binary-basename BINARY_BASENAME Basename of the file against which the coverage will be run
    --arch ARCH                   Architecture to use from universal binaries
    --source-files SOURCE_FILES   A Dir.glob compatible pattern used to limit the lookup to specific source files. Ignored in gcov mode.
    --decimals DECIMALS           The amount of decimals to use for % coverage reporting
    -h, --help                    print help

Một điểm được coi như selling-point của Slather chính là nó có thể output ra file HTML với format khá đẹp đẽ, bằng cách sử dụng option --html như sau:

$ slather coverage --html --scheme XCCov-Demo XCCov-Demo.xcodeproj
Slathering...

To open the html reports, use

open './html/index.html'

or use '--show' flag to open it automatically.

Slathered

Lúc này thư mục làm việc sẽ có thêm thư mục con html, mở file index.html trong folder này để xem code coverage.

Xcode code coverage using Slather

Reference link: https://github.com/SlatherOrg/slather

XCCov

Kể từ Xcode 9.3, Apple đã thêm vào bộ Xcode Command Line Tools một thứ gọi là xccov cho phép view Xcode coverage report. Tuy nhiên, chủ quan đánh giá tool này không cao bằng Slather vì 2 lý do chính: output xấu và dư thông tin. Nhưng bù lại nó thể output ra JSON format, và có thể dùng chung với command này:

$ xcodebuild -project XCCov-Demo.xcodeproj/ -scheme XCCov-Demo -derivedDataPath Build/ -destination 'platform=iOS Simulator,OS=12.2,name=iPhone X' -enableCodeCoverage YES clean build test CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO

Có nghĩa là chúng ta có thể collect code coverage với -enableCodeCoverage YES trong mỗi lần chạy mà không cần configure Scheme như trong đầu bài này.

Reference link: https://github.com/XCTEQ/XCCov-Demo

Kết

Thu thập thông tin Code coverage của một bộ test là bước đầu, mà sau đó thông tin nó mang lại giúp ích trong việc đưa ra quyết định liệu có nên design thêm các tests bổ trợ nhằm nâng cao độ bao phủ code nữa hay không. Quyết định này tuỳ thuộc rất nhiều vào quan điểm của product team. Ví dụ như cùng một con số 90% code coverage, là đáng hài lòng với các product A có teamsize A'; trong khi đó lại không làm thoả mãn các product B với teamsize B'.

Tuy nhiên, có một điều quan trọng mang tính nguy cơ nếu hiểu sai ngay từ đầu. Đó là, 100% code coverage không có nghĩa là 100% tested! Bởi vì, 2 testcases khác nhau có thể cùng đạt được độ bao phủ như nhau, nhưng input của testcase X có thể dẫn đến bug trong khi input của testcase Y thì lại không. Và cũng bởi vì coverage chỉ đo lường một khía cạnh trong rất nhiều khía cạnh của sản phẩm.