单元测试:是检查是否每个代码单元(例如类或函数)是否能产⽣预期
的结果。
单元测试是独⽴运⾏的,不依赖于其他模块或组件。
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
就可以在项目中直接使用单元测试,我们把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
,注意修改自己的项目名称