代码覆盖率入门

1,771 阅读3分钟

WlmE4EMDYy.jpg

什么是代码覆盖率

代码覆盖率,是一种通过计算测试过程中被执行的源代码占全部源代码的比例,进而间接度量软件质量的方法。它在保证测试质量的时候潜在保证实际产品的质量,可以基于此在程序中寻找没有被测试用例测试过的地方,进一步创建新的测试用例来增加覆盖率。按性质,它属于白盒测试的范畴,即主要依据源代码的内部结构来设计测试用例,通过设计不同的输入来测试软件的不同部分。常见的编程语言,如C/C++,python和Java等,都有相应的代码覆盖率测试工具。

本篇是入门篇,带大家入门代码覆盖率,全量代码覆盖率,关于原理、增加代码进行代码覆盖率后面会一一分享

操作步骤

第一步

创建一个demo,名为CodeCoverageRate01

最终目录如下

Xnip2023-11-01_19-47-41.jpg

第二步

添加编译配置,用于生成覆盖率.profraw文件

Xnip2023-11-01_14-56-46.jpg

第三步

确认覆盖率的范围,因为是demo这里是 all targets

Xnip2023-11-01_14-56-29.jpg

第四步

4.1 添加一个 InstrProfiling.h 文件 引进llvm api

因为我的demo是swift项目会提示添加桥接文件,这里不赘述操作过程

#ifndef InstrProfiling_h
#define InstrProfiling_h

  
#ifndef PROFILE_INSTRPROFILING_H_
#define PROFILE_INSTRPROFILING_H_

int __llvm_profile_runtime = 0;
void __llvm_profile_initialize_file(void);
const char *__llvm_profile_get_filename();
void __llvm_profile_set_filename(const char *);
int __llvm_profile_write_file();
int __llvm_profile_register_write_file_atexit(void);
const char *__llvm_profile_get_path_prefix();
#endif /* PROFILE_INSTRPROFILING_H_ */

#endif /* InstrProfiling_h */

4.2 添加CodeCoverageTool类提供API

import Foundation
import UIKit

private let instance:CodeCoverageTool = CodeCoverageTool()

@objc public class CodeCoverageTool:NSObject {
    
    var isRegister:Bool = false
    
    public static func shared() -> CodeCoverageTool {
        return instance
    }
    
    //注册
    func registerCoverage(moduleName: String) {
        if isRegister {
            return
        }
        isRegister = true
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(didEnterBackgroudNotification),
                                               name: UIApplication.didEnterBackgroundNotification,
                                               object: nil)
        
        let name = "\(moduleName).profraw"
        print("registerCoverage, moduleName: \(moduleName)")
        let fileManager = FileManager.default
        do {
            let documentDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
            let filePath: NSString = documentDirectory.appendingPathComponent(name).path as NSString
            print("registerCoverage filePath: \(filePath)")
            __llvm_profile_set_filename(filePath.utf8String)
        } catch {
            print(error)
        }
    }
    
    //合适的时机代码覆盖率上报
    func saveAndUpload() {
        __llvm_profile_write_file()
    }
    
    @objc private func didEnterBackgroudNotification() {
        self.saveAndUpload()
    }
    
}

4.3 添加测试 TestSwift.swift

import Foundation

class TestSwift {
    public func test() {
        print("hello, i`m swift class")
    }
    
    public func test11() {
        print("hello, i`m test11")
    }
}

TestObjc

#import "TestObjc.h"

@implementation TestObjc

- (void)test {
    NSLog(@"hello , i`m objc class");
}

- (void)test11 {
    NSLog(@"hello , i`m test11");
}

@end

在ViewController.swift里调用

import UIKit


class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        test()
        
    }
    
    @objc private func test() {
        
        TestObjc().test()
        TestSwift().test()
    }
}

第五步

业务使用

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        CodeCoverageTool.shared().registerCoverage(moduleName: "testCodeCoverage")
        
        return true
}

运行APP,随便操作几下,退到后台

第六步

捞取.profraw文件 步骤 xcode -> windows -> devices and simulators Xnip2023-11-01_19-55-12.jpg

下载后,点击查看包内容,如下可以拿到.profraw文件 Xnip2023-11-01_19-55-52.jpg

第七步

合成.testCodeCoverage.profdata

首先把.profraw和对应的二进制文件放在一个文件夹里,如下 Xnip2023-11-01_19-57-16.jpg

执行如下指令

xcrun llvm-profdata merge -sparse testCodeCoverage.profraw -o testCodeCoverage.profdata

生成.profdata文件

Xnip2023-11-01_19-58-30.jpg

第八步

生成报告

执行如下指令

xcrun llvm-cov show [二进制文件路径] --instr-profile=testCodeCoverage.profdata  --format=html  -use-color --output-dir ./coverage_report

[二进制文件路径]我的为 /Users/gegaozhao/Library/Developer/Xcode/DerivedData/CodeCoverageRate01-ahzrkhfjrajjnjaolknrsjgxmabw/Build/Products/Debug-iphoneos/CodeCoverageRate01.app/CodeCoverageRate01

效果

Xnip2023-11-01_20-00-55.jpg 点击index.html,跳转到浏览器

Xnip2023-11-01_20-01-59.jpg

使用

第一步:podfile引入GZCodeCoverageKit库

source 'https://github.com/CocoaPods/Specs.git'

target 'XXX' do
  pod 'GZCodeCoverageKit' #不需要指定版本,越新越稳定
end

第二步:podfile添加覆盖率配置

post_install do |installer|
  installer.generated_projects.each do |project|
    project.targets.each do |target|
      target.build_configurations.each do |config|
        if config.name == 'Debug'
           # 覆盖率配置
           config.build_settings['OTHER_CFLAGS'] = '$(inherited) -fprofile-instr-generate -fcoverage-mapping'
           config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited) -profile-generate -profile-coverage-mapping'
           config.build_settings['OTHER_LDFLAGS'] = '$(inherited) -fprofile-instr-generate'
           # End of 覆盖率配置
        end
      end
    end
  end
end

# 修改主工程
project = Xcodeproj::Project.open('XXX.xcodeproj')
puts project
project.targets.each do |target|
  if target.name == 'XXX'
    target.build_configurations.each do |config|
      if config.name == 'Debug'
        # 覆盖率配置
        config.build_settings['OTHER_CFLAGS'] = '$(inherited) -fprofile-instr-generate -fcoverage-mapping'
        config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited) -profile-generate -profile-coverage-mapping'
        config.build_settings['OTHER_LDFLAGS'] = '$(inherited) -fprofile-instr-generate'
        # End of 覆盖率配置
      end
    end
  end
end
project.save()

注:XXX为工程的target名字

GZCodeCoverageKit信息

剩余步骤从【操作步骤】的第五步开始即可...

最后

到这里流程就讲完了,感谢阅读!