单元测试:是检查是否每个代码单元(例如类或函数)是否能产⽣预期
的结果。
单元测试是独⽴运⾏的,不依赖于其他模块或组件。
FIRST:
- Fast:测试模块应该是快速⾼效的;
- Independent/Isolated:测试模块应该是独⽴相互不影响的;
- Repeatable :测试实例应该是可以重复使⽤的,测试结果应该是相同的;
- Self-validating:测试应完全⾃动化。输出结果要么是“成功”,要么是“失败”;
- Timely:理想情况下,应该在编写要测试的⽣产代码之前编写测试(测试驱动开发)。
单元测试需要启动APP。所以每次都会进行加载,产生耗时操作,所以这个时候就需要在测试的启动自己的FakeAppDelegate。
这个时候我们可以从main函数中进行调试
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
原理只要将appDelegateClassName换成我们自己的测试FakeAppDelegate就可以了,但是会将自己整成的程序污染,这个时候就需要NSStringFromClass,NSStringFromClass只有被编译的类才会查找到,而UnitTest类只有测试时才会参与编译,所以我们可以改成
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// 方法 字符串->class
id cls = NSClassFromString(@"XCTest");
appDelegateClassName = cls ? @"FakeAppDelgate" : NSStringFromClass([AppDelegate class]);
}
// 进行单元测试的时候 FakeAppDelgate
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
swift中也是同理
新建main.swift
let cls = NSClassFromString("XCTest") != nil
let appDelegateClassName = cls ? NSStringFromClass(LGFakeAppDelegate.self) : NSStringFromClass(AppDelegate.self)
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, appDelegateClassName)
编译完成之后我们我们打开app包所在位置,除了我们应用的app包还有个测试包

里面有几个动态库,我们的单元测试是不是有个方法进入,我们引入这个库,位置是
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks我们通过
xcconfig来引入
// 1. header
HEADER_SEARCH_PATHS = $(inherited) "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/XCTest.framework/Headers"
//2.链接动态库
// 传统方式
OTHER_LDFLAGS = $(inherited) -F"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks" -framework "XCTest"
LD_RUNPATH_SEARCH_PATHS = $(inherited) "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks"
这个时候我们只要导入#import <XCTest/XCTest.h>就可以在项目中直接使用单元测试,我们把test.m中的内容拷贝进入。
XCTestSuite *suite = [XCTestSuite defaultTestSuite];
// 初始化 testCase
LoginAppUITests *testCase = [LoginAppUITests new];
// testCase -》 suite
[suite addTest:testCase];
for (XCTest *test in suite.tests) {
[test runTest];
}
如果直接创建就相当于执行一个单元测试,测试完就会崩溃,主要是[XCTestSuite defaultTestSuite]只能执行一次。
//起个别名
XCTestSuite *suite = [XCTestSuite testSuiteWithName:@"MyTest"];
//指定某个方法
[suite addTest:[LoginAppUITests testCaseWithSelector:@selector(testExample)]];
for (XCTest *test in suite.tests) {
[test runTest];
}
}
OC&swift测试
swiftTest调用OC方法
新建一个测试文件swiftTest,会自动创建UnitTest工程的桥接文件,再在主工程创建swift类生成主工程的桥接文件,在主工程桥接文件中导入创建的类,在swiftTest导入主工程模块,就可以直接使用了。

如果要测试swift代码就需要@testable import "testApp",这样就可以支持测试open,public, internal权限的代码
OC调用swift方法
直接#import "testApp-Swift.h"会提示无法找到,这时候需要在TARGET找到单元测试的target,在buildsetting中的Header Search Path中将路径添加进去$CONFIGURATION_TEMP_DIR/testApp.build/DerivedSources,注意修改自己的项目名称