iOS 测试 | iOS 自动化性能采集

摘要:
今天小编跟大家分享一篇来自学院内部学员的技术分享,本文主要介绍了作者在进行iOS自动化性能采集的一些经验,希望对大家在进行iOS自动化测试时有一些启发。

​今天小编跟大家分享一篇来自学院内部学员的技术分享,本文主要介绍了作者在进行 iOS 自动化性能采集的一些经验,希望对大家在进行 iOS 自动化测试时有一些启发。

不要为小事遮住视线,我们还有更大的世界

iOS 测试 | iOS 自动化性能采集第1张

前言

对于iOS总体生态是比较封闭的,相比Android没有像adb这种可以查看内存、cpu的命令.在日常做性能测试,需要借助xcode中instruments查看内存、cpu等数据.

但是借助instruments比较麻烦、又不能提供命令行.在持续集成中,很难时时的监控app的性能指标.并且现在app发版一般是2周左右,留给做专项测试的时间更少了,那么做核心场景性能测试,肯定是来不及的.

所以需要借助一些自动化工具来减轻手工采集性能指标的工作量.

性能采集项

app中基本性能采集项,内存、cpu、fps、电量等,因为自动化采集中手机设备是插着电脑充电的,所以不能采集电量数据.

已有工具

  • instruments是官方提供的,不能做到自动化采集

  • 腾讯gt,需要在app中集成sdk,有一定的接入成本

  • 第三sdk,类似腾讯gt需要在app集成,可能会有数据泄漏风险

脚本开发

上述的已有工具都不满足,在持续集成中做到自动化采集性能数据,期望的性能测试工具有一下几点:

  • 方便接入

  • 可生成性能报告

  • 可持续化

  • 数据收集精准

所以基于这几点,需要自己开发一套性能采集脚本.

使用官方提供的api做性能采集

获取内存、cpu等

#import <mach/mach.h>
/**
* 获取内存
*/
- (NSString *)get_memory {
int64_t memoryUsageInByte = 0;
task_vm_info_data_t vmInfo;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t kernelReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
if(kernelReturn == KERN_SUCCESS) {
memoryUsageInByte = (int64_t) vmInfo.phys_footprint;
NSLog(@"Memory in use (in bytes): %lld", memoryUsageInByte);
} else {
NSLog(@"Error with task_info(): %s", mach_error_string(kernelReturn));
}
double mem = memoryUsageInByte / (1024.0 * 1024.0);
NSString *memtostring ;
memtostring = [NSString stringWithFormat:@"%.1lf",mem];
return memtostring;
}
/**
* 获取cpu
*/
- (NSString *) get_cpu{
kern_return_t kr;
task_info_data_t tinfo;
mach_msg_type_number_t task_info_count;
task_info_count = TASK_INFO_MAX;
kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)tinfo, &task_info_count);
if (kr != KERN_SUCCESS) {
return [ NSString stringWithFormat: @"%f" ,-1];
}
task_basic_info_t basic_info;
thread_array_t thread_list;
mach_msg_type_number_t thread_count;
thread_info_data_t thinfo;
mach_msg_type_number_t thread_info_count;
thread_basic_info_t basic_info_th;
uint32_t stat_thread = 0; // Mach threads
basic_info = (task_basic_info_t)tinfo;
// get threads in the task
kr = task_threads(mach_task_self(), &thread_list, &thread_count);
if (kr != KERN_SUCCESS) {
return [ NSString stringWithFormat: @"%f" ,-1];
}
if (thread_count > 0)
stat_thread += thread_count;
long tot_sec = 0;
long tot_usec = 0;
float tot_cpu = 0;
int j;
for (j = 0; j < thread_count; j++)
{
thread_info_count = THREAD_INFO_MAX;
kr = thread_info(thread_list[j], THREAD_BASIC_INFO,
(thread_info_t)thinfo, &thread_info_count);
if (kr != KERN_SUCCESS) {
tot_cpu = -1;
//return -1;
}
basic_info_th = (thread_basic_info_t)thinfo;
if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds;
tot_usec = tot_usec + basic_info_th->user_time.microseconds + basic_info_th->system_time.microseconds;
tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE * 100.0;
}
} // for each thread
kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
assert(kr == KERN_SUCCESS);
NSString *tostring = nil ;
tostring = [ NSString stringWithFormat: @"%.1f" ,tot_cpu];
NSLog (@"performance cpu:%@",tostring);
return tostring;
}

获取页面vc

上边收集了内存和cpu,还需要在收集数据的同时和页面对应上.这样就清楚了是当前页面的内存和cpu情况.

/**
*获取当前vc
*/
- (UIViewController *) get_vc {
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
if ([keyWindow.rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tab = (UITabBarController *)keyWindow.rootViewController;
UINavigationController *nav = tab.childViewControllers[tab.selectedIndex];
DDContainerController *content = [nav topViewController];
weakSelf.vc = [content contentViewController];
}
});
return self.vc;
}

获取设备信息

/*
*获取设备名称
*/
- (NSString *) get_devicesName {
NSString *devicesName = [UIDevice currentDevice].name; //设备名称
NSLog(@"performance devicesName:%@", devicesName);
return devicesName;
}
/*
*获取系统版本
*/
- (NSString *) get_systemVersion{
NSString *systemVersion = [UIDevice currentDevice].systemVersion; //系统版本
NSLog(@"performance version:%@", systemVersion);
return systemVersion;
}
/*
*获取设备idf
*/
- (NSString *) get_idf {
NSString *idf = [UIDevice currentDevice].identifierForVendor.UUIDString;
NSLog(@"performance idf:%@", idf);
return idf;
}

数据拼接

最终要把内存、cpu等数据拼接成字典的形式,方便输出查看

输出log日志的数据格式
{
"cpu": "0.4",
"fps": "60 FPS",
"version": "11.2",
"appname": "xxxxxx",
"battery": "-100.0",
"appversion": "5.0.4",
"time": "2018-09-07 11:45:24",
"memory": "141.9",
"devicesName": "xxxxxx",
"vcClass": "DDAlreadPaidTabListVC",
"idf": "8863F83E-70CB-43D5-B6C7-EAB85F3A2AAD"
}

开启子线程采集

开一个子线程定时采集数据
/*
* 性能采集子线程
*/
- (void) performancethread {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"performance ======get performance======");
[self get_fps];
while (true) {
DDPerformanceModel *model = [DDPerformanceModel new];
model.time=[self get_time];
model.appname=[self get_appname];
model.appversion=[self get_appversion];
model.idf =[self get_idf];
model.devicesName =[self get_devicesName];
model.version = [self get_systemVersion ];
model.vcClass = NSStringFromClass([self get_vc].class);
model.memory = [self get_memory];
model.battery = [self get_battery];
model.cpu = [self get_cpu];
model.fps = self.percount;
NSString *json = [model modelToJSONString];
// printf(" getperformance %s ", [json UTF8String]);
NSLog(@"getperformance model %@", json);
sleep(5);
}
}];
[thread start];
NSLog(@"performance ======continue mainblock======");
}

初始化性能采集

AppDelegate.m文件中didFinishLaunchingWithOptions方法中用户各种初始化操作,可以在第一行初始化性能采集,
这样app启动以后就可以定时采集数据
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[getperformance new] performancethread];//获取性能数据
}

性能采集日志存储

一般来说日志存储都是写入到本地log日志,然后读取.但是有两个问题

  • 需要读写文件代码,对于不熟悉oc的人来说比较难

  • 因为是定时采集,文件IO操作频繁

所以不考虑存储本地log日志的方式,可以在代码中打印出数据,通过截获当前设备运行的日志获取数据.

模拟器可以使用xcrun simctl命令获取当前设备运行日志,
真机用libimobiledevice获取日志
xcrun simctl spawn booted log stream --level=debug | grep getperformance
输出log日志的数据格式,这块做了json美化,每歌几秒在控制台就打印一次
{
"cpu": "0.4",
"fps": "60 FPS",
"version": "11.2",
"appname": "xxxxxx",
"battery": "-100.0",
"appversion": "5.0.4",
"time": "2018-09-07 11:45:24",
"memory": "141.9",
"devicesName": "xxxxxx",
"vcClass": "DDAlreadPaidTabListVC",
"idf": "8863F83E-70CB-43D5-B6C7-EAB85F3A2AAD"
}
如果获取多次数据可以使用shell脚本把命令放到后台,定时写入到logpath中
nohup xcrun simctl spawn booted log stream --level=debug >${logpath} &

代码插入到工程中

因为在持续集成中,每次打取的代码都是不带性能测试代码,这些代码是单独写到文件中.在编译项目前,用shell把代码插入到工程中,这样打出来的包才能有采集性能数据功能.

scriptrootpath=${2}
AddFiles=${2}"/GetPerformance/performancefiles"
localDDPerformanceModelh=${scriptrootpath}"/GetPerformance/performancefiles/DDPerformanceModel.h"
localDDPerformanceModelm=${scriptrootpath}"/GetPerformance/performancefiles/DDPerformanceModel.m"
localgetperformanceh=${scriptrootpath}"/GetPerformance/performancefiles/getperformance.h"
localgetperformancem=${scriptrootpath}"/GetPerformance/performancefiles/getperformance.m"
addfiles(){
echo "删除${projectaddpath}中的原性能采集文件"
rm -rf ${DDPerformanceModelh}
rm -rf ${DDPerformanceModelm}
rm -rf ${getperformanceh}
rm -rf ${getperformancem}
echo "复制文件到${projectaddpath}路径"
cp ${localDDPerformanceModelh} ${projectaddpath}
cp ${localDDPerformanceModelm} ${projectaddpath}
cp ${localgetperformanceh} ${projectaddpath}
cp ${localgetperformancem} ${projectaddpath}
}

性能数据绘制

在手工和自动化使用插入性能测试代码的app,如果截获性能数据后,可以对数据做性能数据绘制.

用Higcharts或者echarts绘制性能走势图

iOS 测试 | iOS 自动化性能采集第2张

如何在持续集成中使用

monkey和UI自动化中使用,最终会发送一份性能报告.

Demo代码

已经把性能代码脱了主项目,可在Demo代码中编译,github地址:https://github.com/xinxi1990/iOSPerformanceTest

最后

虽然iOS生态封闭,但是对于开发者和测试者还是有一些空间可以利用的.

iOS测试一直都是一个难点,难懂的oc语法和iOS整体框架.如果你开始慢慢接触iOS,会发现iOS测试也并不是那么难,需要一点耐心和一点专心而已.

(文章来源于霍格沃兹测试学院)

更多优秀内容及资料可点击获取

http://qrcode.testing-studio.com/f?from=bokeyuan&url=https://ceshiren.com/t/topic/3595

免责声明:文章转载自《iOS 测试 | iOS 自动化性能采集》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇设计模式--观察者模式初探和java Observable模式老vue项目webpack3升级到webpack5全过程记录(一)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Qt中的打印操作

Qt中对打印的支持是有一个独立的printsupport模块来完成的,所以,要想在程序中使用Qt的打印功能,必须先在pro文件中添加下面这句代码: QT += printsupport在这个模块中,提供了八个类来支持打印功能。我们来简单的看几个与打印有直接关系的几个类:QPageSetupDialog:该类可以用来对打印页面进行一些相关的配置,如页面方向,...

ubuntu和centos操作命令对比

ubuntu和centos操作命令对比 1,安装软件,比如安装jdk CentOS : rpm -i jdk-XXX_linux-x64_bin.rpm 进行安装, Ubuntu : dpkg -i jdk-XXX_linux-x64_bin.deb 进行安装。 其中 -i 就是 install 的意思。 2,管理软件 查询已安装的软件 rpm -qa...

iOS App 内部跳转(设置、Wifi、蓝牙...)关键词

1、iOS 10 以前: 蜂窝网络:prefs:root=MOBILE_DATA_SETTINGS_ID Wi-Fi:prefs:root=WIFI 定位服务:prefs:root=LOCATION_SERVICES 个人热点:prefs:root=INTERNET_TETHERING 关于本机:prefs:root=General&path=Ab...

iOS 上架记录:Other

背景:本来就是一个记账的简单的应用,又添加了一些h5的页面,和一些弹框广告。 过程:在iOS上架之后,一直因为 Guideline 3.2.2 - Business - Other Business Model Issues - Unacceptable 被拒绝! 拒绝了四次,每次都是按照要求去修改,还是被拒绝!!! 时间:2020.04.22; 第五次,...

Celery学习--- Celery在项目中的使用

可以把celery配置成一个应用,注意连接文件命名必须为celery.py 目录格式如下 项目前提: 安装并启动Redis CeleryPro/celery.py   【命名必须为celery.py】 # 将相对路径转换为绝对路径 from __future__ import absolute_import, unicode_literals...

[无线路由] “免费”斐讯K2路由器刷OpenWRT(实战MWAN多宽带网速叠加)

(阿财首发于什么值得买)斐讯K2可以算是一个非常另类的跨界数码产品,其产品完全的醉翁之意不在酒。最多值99元的 MT7260硬件架构和用料,售价399元,金额激活K码后自动转入合作理财P2P平台,等待一个月可以全额返还路由器金额的“免费”模式,让阿财的小 伙伴们在群里讨论差不多大半个月。心痒痒又怕上了贼船,因为阿财的家中实际上有2条宽带 ——线路一、联通2...