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

单元测试(二)

作者: 浅墨入画 | 来源:发表于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://www.haomeiwen.com/subject/ptoecltx.html