美文网首页
单元测试(二)

单元测试(二)

作者: 浅墨入画 | 来源:发表于2021-03-20 10:16 被阅读0次

一. OC&Swift测试工程配置

创建包含单元测试的OC工程TestApp,在TestAppTests目录下创建Swift单元测试文件TestAppSwift.swift,Xcode会提示创建桥接文件TestAppTests-Bridging-Header.h,我们选择创建,创建完目录如下
注意: 此时的桥接文件只在TestAppTests下生效

image.png

接下来我们在TestApp目录下创建Swift文件SwiftApp.swift,Xcode也会提示创建桥接文件TestApp-Bridging-Header.h,我们选择创建,目录如下

image.png

1.1 测试OC类中引用工程OC类
测试类中引入工程OC类头文件即可使用

//TestAppTests.m内容
#import <XCTest/XCTest.h>
#import "ViewController.h"

@interface TestAppTests : XCTestCase
@end
@implementation TestAppTests

- (void)setUp {
}

- (void)testExample {
    ViewController *vc = [ViewController new];
}
@end
// Cmd + U 测试成功

1.2 测试Swift类中引用工程OC类
现在我们想在TestAppTests目录下TestAppSwift.swift文件中使用ViewController,需要修改文件内容如下

// 第一步 TestApp目录下桥接文件TestApp-Bridging-Header.h中导入ViewController
// TestApp-Bridging-Header.h内容
#import "ViewController.h"
// 第二步 TestAppTests目录下修改TestAppSwift.swift文件内容
import XCTest
import TestApp  //导入module的方式引入

class TestAppSwift: XCTestCase {

    func testExample() throws {
        let vc = ViewController()
    }
}
// Cmd + U 测试成功

1.3 测试Swift类中引用工程Swift类
我们在SwiftApp.swift文件中创建两个模型

// SwiftApp.swift内容
import Foundation

@objc open class SwiftModel: NSObject {
    var num: Int?
}

class SwiftModel2: NSObject {
}

这个时候我们在TestAppSwift.swift文件引用SwiftModel与SwiftModel2,会发现SwiftModel2找不到,这时就需要用到@testable

//TestApp目录下创建的属性为internal的类,需要使用@testable才能在TestAppTests中引用
import XCTest
@testable import TestApp

class TestAppSwift: XCTestCase {

    func testExample() throws {
        let vc = ViewController()
        let model = SwiftModel()
        let model2 = SwiftModel2()
    }
}
// Cmd + U 测试成功

1.4 工程OC类中引用工程Swift类
工程OC类中引入TestApp-Swift.h即可使用

//ViewController.m内容
#import "ViewController.h"
#import "TestApp-Swift.h"

@interface ViewController ()
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    SwiftModel *model = [SwiftModel new];
    SwiftModel2 *model2 = [SwiftModel2 new];
    // Do any additional setup after loading the view.
}
@end
// Cmd + B 编译成功

1.5 测试OC类中引用工程Swift类
TestApp-Swift.h文件是在TestApp生成的,所以需要在TestAppTests中配置TestApp-Swift.h路径,现在需要找到TestApp-Swift.h路径,选中Products目录下TestApp.app,右键show in finder,可以看出TestApp在Products->Debug-iphonesimulator目录下

image.png

此时选中Intermediates.noindex目录往下找,会发现estApp-Swift.h在DerivedSources目录下

image.png

找到路径之后开始配置,其中Debug-iphonesimulator目录即为CONFIGURATION_TEMP_DIR,配置内容为CONFIGURATION_TEMP_DIR/TestApp.build/DerivedSources,
如下图

image.png

测试OC类中引入TestApp-Swift.h即可使用

// TestAppTests.m内容
#import <XCTest/XCTest.h>
#import "ViewController.h"
#import "TestApp-Swift.h"

@interface TestAppTests : XCTestCase
@end
@implementation TestAppTests

- (void)setUp { 
}

- (void)testExample {
    ViewController *vc = [ViewController new];
    SwiftModel *model = [SwiftModel new];
    SwiftModel2 *model2 = [SwiftModel2 new];
}
@end
// Cmd + U 测试成功

注意:非递归non-recursive与递归recursive

image.png

二. HeaderMap原理&读取

2.1 手动查看.hmap结构内容
打开我们的DumpHeaderMap工程,把上面例子中的.hmap文件复制到工程根目录下,Edit Scheme 中配置.hmap文件如下

image.png

main函数中打上断点,Cmd + R运行工程

image.png
// 参数一  当前可执行文件的路径
// 参数二  配置的.hmap文件路径
(lldb) parray 2 argv 
(const char **) $0 = 0x00007ffeefbff390 {
  (const char *) [0] = 0x00007ffeefbff598 "/Users/wangning/Library/Developer/Xcode/DerivedData/DumpHeaderMap-gtvyguhkmfjhahbpigfnkdwbquht/Build/Products/Debug/DumpHeaderMap"
  (const char *) [1] = 0x00007ffeefbff61a "/Users/wangning/Documents/资料/3:16/第十六节课、单元测试与UI测试/上课代码/02-HeaderMap原理&读取/DumpHeaderMap/LGTestApp-project-headers.hmap"
}

接下来在这里打上断点,执行断点

image.png
// 继续执行打印如下
// String table entry count: 5,说明有5个头文件
Header map: /Users/wangning/Documents/资料/3:16/第十六节课、单元测试与UI测试/上课代码/02-HeaderMap原理&读取/DumpHeaderMap/LGTestApp-project-headers.hmap
    Hash bucket count: 8
    String table entry count: 5
    Max value length: 0 bytes
// 跳过断点打印如下
// 主题
// Key LGTestApp-Bridging-Header.h
// 头文件的前半部分
// /Users/ws/Desktop/VIP课程/第十六节课、单元测试与UI测试/上课代码/01-OC&Swift测试工程配置/LGTestApp/LGTestApp/
//头文件的后半部分
// Suffix LGTestApp-Bridging-Header.h
// hmap 对应 按照规则存储一堆的头文件
- Bucket 1: Key ViewController.h -> Prefix /Users/ws/Desktop/VIP课程/第十六节课、单元测试与UI测试/上课代码/01-OC&Swift测试工程配置/LGTestApp/LGTestApp/, Suffix ViewController.h
    - Bucket 2: Key AppDelegate.h -> Prefix /Users/ws/Desktop/VIP课程/第十六节课、单元测试与UI测试/上课代码/01-OC&Swift测试工程配置/LGTestApp/LGTestApp/, Suffix AppDelegate.h
    - Bucket 3: Key SceneDelegate.h -> Prefix /Users/ws/Desktop/VIP课程/第十六节课、单元测试与UI测试/上课代码/01-OC&Swift测试工程配置/LGTestApp/LGTestApp/, Suffix SceneDelegate.h
- Bucket 6: Key LGTestAppTests-Bridging-Header.h -> Prefix /Users/ws/Desktop/VIP课程/第十六节课、单元测试与UI测试/上课代码/01-OC&Swift测试工程配置/LGTestApp/LGTestAppTests/, Suffix LGTestAppTests-Bridging-Header.h
    - Bucket 7: Key LGTestApp-Bridging-Header.h -> Prefix /Users/ws/Desktop/VIP课程/第十六节课、单元测试与UI测试/上课代码/01-OC&Swift测试工程配置/LGTestApp/LGTestApp/, Suffix LGTestApp-Bridging-Header.h

2.2 终端配置DumpHeaderMap,查看.hmap结构内容
接下来我们想让可执行文件DumpHeaderMap像终端lldb那样来使用需要怎么办?

  • 首先我们找到可执行文件DumpHeaderMap,并且在用户 -> wn 目录下创建如下文件,放入DumpHeaderMap
image.png
  • 修改配置文件如下
// 编辑bashrc文件
$ vim ~/.bashrc
image.png
// 让配置生效
$ vim ~/.bashrc

此时终端使用DumpHeaderMap命令即可生效

image.png

我们发现有的文件是相对路径,有的是绝对路径,原因是Build Phases -> Headers下面,头文件为Public/Private属性都为相对路径,只有头文件为Project属性才为绝对路径。

三. 生成HeaderMap文件

3.1 测试手动生成hmap文件
使用上面TestApp工程,找到TestApp-Swift.h文件,复制到TestApp工程根目录下
打开WriteHeaderMap工程,修改write方法如下

void write() {
    struct HMapHeader header;
    // 8 :指定几个Bucket
    // 750: 指定buffer大小
    typedef MapFile<8, 750> FileTy;
    FileTy File;
    File.init();
    FileMaker<FileTy> Maker(File);
    // 1.添加第一个Bucket
    //  添加key
    auto a = Maker.addString("TestApp-Swift.h");
    // 前半部分
    auto b = Maker.addString("/Users/wn/Desktop/TestApp/");
    // 后半部分
    auto c = Maker.addString("TestApp-Swift.h");
    Maker.addBucket(getHash(@"TestApp-Swift.h"), a, b, c);

    auto ViewControllera = Maker.addString("ViewController.h");
    auto ViewControllerb = Maker.addString("/Users/wn/Desktop/TestApp/TestApp");
    auto ViewControllerc = Maker.addString("ViewController.h");
    Maker.addBucket(getHash(@"ViewController.h"), ViewControllera, ViewControllerb, ViewControllerc);
    
    auto Bridginga = Maker.addString("TestApp-Bridging-Header.h");
    auto Bridgingb = Maker.addString("/Users/wn/Desktop/TestApp/TestApp");
    auto Bridgingc = Maker.addString("TestApp-Bridging-Header.h");
    Maker.addBucket(getHash(@"TestApp-Bridging-Header.h"), Bridginga, Bridgingb, Bridgingc);
    auto ScrollViewa = Maker.addString("TestAppTests-Bridging-Header.h");
    auto ScrollViewb = Maker.addString("/Users/wn/Desktop/TestApp/LGTestAppTests/");
    auto ScrollViewc = Maker.addString("TestAppTests-Bridging-Header.h");
    Maker.addBucket(getHash(@"TestAppTests-Bridging-Header.h"), ScrollViewa, ScrollViewb, ScrollViewc);

    NSData *data = File.getBuffer();
    [data getBytes:&header length:sizeof(struct HMapHeader)];
    // bucket写入路径
    [data writeToFile:@"/Users/wn/Desktop/TestApp/TestApp/mm.hmap" atomically:YES];
    bool is_swapped = false;
    uint32_t (*swap)(uint32_t) = nop_swap;
    if (header.Magic == HMAP_HeaderMagicNumber
        && header.Version == HMAP_HeaderVersion) {
        is_swapped = false;
    } else if (header.Magic == HMAP_SwappedMagic
        && header.Version == HMAP_SwappedVersion) {
        is_swapped = true;
        swap = actually_swap;
    } else {
        warn(/*EX_DATAERR,*/ "header lacks HMAP magic");
        return;
    }
    const uint32_t bucket_count = swap(header.NumBuckets);
    printf(
        "\tHash bucket count: %" PRIu32 "\n"
        "\tString table entry count: %" PRIu32 "\n"
        "\tMax value length: %" PRIu32 " bytes\n",
        bucket_count,
        swap(header.NumEntries),
        swap(header.MaxValueLength));
    const char *raw = (const char *)data.bytes;
    const struct HMapBucket *const buckets = (const struct HMapBucket *const)(raw
            + sizeof(struct HMapHeader));
        const char *const string_table = (raw
            + sizeof(struct HMapHeader)
            + bucket_count*sizeof(struct HMapBucket));
    for (uint32_t i = 0; i < bucket_count; ++i) {
        const struct HMapBucket *const bucket = &(buckets[i]);
        if (swap(bucket->Key) == HMAP_EmptyBucketKey) { continue; }
        // LLVM is careful to sanity-check bucket-count and strlen,
        // but we're just going to assume they're all NUL-terminated
        // and not maliciously crafted input files.
        //
        // This is naive, but this is also not exactly "production" code.
        const char *key = string_table + swap(bucket->Key);
        const char *prefix = string_table + swap(bucket->Prefix);
        const char *suffix = string_table + swap(bucket->Suffix);

        NSLog(@"\t- Bucket %" PRIu32 ": "
            "Key %s -> Prefix %s, Suffix %s\n",
            i,
            key, prefix, suffix);
    }
}
// 打印可以看出,bucket写入mm.hmap文件
Hash bucket count: 2
String table entry count: 1
Max value length: 0 bytes
2021-03-19 23:09:50.529531+0800 WriteHeaderMap[63414:6244069]   - Bucket 0: Key LGTestApp-Swift.h -> Prefix /Users/wangning/Desktop/TestApp/, Suffix TestApp-Swift.h

TestAppTests配置mm.hmap文件路径如下图

image.png

Cmd + U 测试成功,说明把TestApp-Swift.h写入mm.hmap文件,Header Search Paths 引入mm.hmap的方式,也能找到TestApp-Swift.h

相关文章

  • 单元测试

    一.单元测试是什么 二.单元测试可以做什么 三.单元测试不能做什么 四.为什么要做单元测试 参考资料: https...

  • 语文作业

    1、完成单元测试二单元测试题(不空题) 2、背会并掌握二单元测试页子试题,家长提问。 3、作文本上写作文:难忘的_...

  • 软件测试类型

    按阶段划分 一.单元测试 二.集成测试 三.系统测试 四.验收测试 按阶段划分-单元测试 什么是单元测试(Unit...

  • 2018-09-18

    讲评第二单元测试卷

  • 测试过程

    一、测试过程简介 单元测试 集成测试 系统测试 二、测试过程单元集成系统及比较 1.单元测试--函数 单元测试时针...

  • 超级详细的Junit单元测试教程

    文章目录 Junit单元测试 一、什么是单元测试? 二、单元测试的重要性 三、黑盒测试与白盒测试 3.1 黑盒测试...

  • Swift-单元测试

    以下链接是之前写的一个单元测试连载文,在此记录下,方便查找 swift单元测试(一)基本概念swift单元测试(二...

  • Android单元测试(一)-基础

    一、什么是单元测试 单元测试是测试某个类的某个方法能否正常工作的一种手段。 二、单元测试目的 验收(改动和重构) ...

  • Robolectric框架的使用

    可以参考地址: 1.Android单元测试(一):JUnit框架的使用 2.Android单元测试(二):Mock...

  • 内存中完成:图片压缩+Base64编码

    一、引入thumbnailator maven: gradle: 二、单元测试代码

网友评论

      本文标题:单元测试(二)

      本文链接:https://www.haomeiwen.com/subject/ptoecltx.html