iOS自动化测试之KIF使用分享

KIF的全称是Keep it functional。它是一个建立在XCTest的UI测试框架,通过accessibility来定位具体的控件,再利用私有的API来操作UI。由于是建立在XCTest上的,所以你可以完美的借助XCode的测试相关工具(包括命令行脚本)。

特点:

  • 最小化迂回时间
    继承KIFTestCase,测试代码都是使用OC编写,最大程度减少了中间层。
  • 配置简单
    直接集成到XCode上,不需要安装多余的包。
  • 像用户一样测试
    测试代码模仿用户操作,代码很简单
  • 自动集成XCode 5以上的测试工具
    在XCode上使用就像使用苹果原生的测试框架一样,支持XCode的各种测试工具。

按照KIF的的官方文档进行导入KIF,接下来以官方的demo为例,进行KIF的相关操作的讲解。

目录

  • 使用描述
  • Tapping
  • Show/Hide
  • Gesture
  • TableView
  • Picker
  • ModalView
  • CollectionView
  • ScrollView
  • Landscape
  • WebView
  • Background
  • PullToRefesh
  • CascadingFailure
  • 补充Tips

使用描述

使用KIF主要有两个核心类:

  • KIFTestCase XCTestCase的子类
  • KIFUITestActor 控制UI,常见的三种是:点击一个View,向一个View输入内容,等待一个View的出现

KIF利用Accessibility来找元素.tapViewWithAccessibilityLabel 这也许是最常被用到的测试动作方法。正如其名称所显示的,它可以在给定的辅助标签模拟在视图上的触击。在大多数情况下,辅助标签和可视的文本标签(例如按钮组件)是配套的。否则你就需要手动设置辅助标签.
一些控件,诸如 UISwitch,更加复杂,需要比简单的触击更复杂的步骤来触发。KIF 提供了一个特殊的 setOn:forSwitchWithAccessibilityLabel: 方法来改变一个切换的状态.

导入KIF框架后,在项目的Tests(如果还没有Tests文件夹,可以先创建Tests)声明一个测试类,必须以Tests结尾(如LoginTests),继承自KIFTestCase。为了让程序在运行每个测试用例时能保证连贯性,在每个类中都声明两个方法beforeEachafterEach 保证测试过程中可以进入想要执行的测试页面与退出测试页面。

///进入指定页面
- (void)beforeEach{
    [tester tapViewWithAccessibilityLabel:@"Tapping"];
}
///退出指定页面
- (void)afterEach{
    [tester tapViewWithAccessibilityLabel:@"Test Suite" traits:UIAccessibilityTraitButton];
}

Tapping

点击控制器上标记为TapViewController Inner ScrollView的视图上的元素

- (void)testTappingViewFromSpecificView{
    UIView *scrollView = [tester waitForViewWithAccessibilityIdentifier:@"TapViewController Inner ScrollView"];
    UIView *buttonView;
    UIAccessibilityElement *element;
    [tester waitForAccessibilityElement:&element view:&buttonView withIdentifier:@"Inner Button" fromRootView:scrollView tappable:YES];
    
    if (buttonView != NULL) {
        [tester tapAccessibilityElement:element inView:buttonView];
    }
}

长按操作

- (void)testLongPressingViewViewWithTraits{
    [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" duration:2];
    [tester tapViewWithAccessibilityLabel:@"Select All"];
}

切换按钮状态

- (void)testTogglingASwitch
{
    [tester waitForViewWithAccessibilityLabel:@"Happy" value:@"1" traits:UIAccessibilityTraitNone];
    [tester setOn:NO forSwitchWithAccessibilityLabel:@"Happy"];
    [tester waitForViewWithAccessibilityLabel:@"Happy" value:@"0" traits:UIAccessibilityTraitNone];
    [tester setOn:YES forSwitchWithAccessibilityLabel:@"Happy"];
    [tester waitForViewWithAccessibilityLabel:@"Happy" value:@"1" traits:UIAccessibilityTraitNone];
}

滑动状态条

- (void)testMovingASlider{
    [tester waitForTimeInterval:1];
    [tester setValue:3 forSliderWithAccessibilityLabel:@"Slider"];
    [tester waitForViewWithAccessibilityLabel:@"Slider" value:@"3" traits:UIAccessibilityTraitNone];
    [tester setValue:0 forSliderWithAccessibilityLabel:@"Slider"];
    [tester waitForViewWithAccessibilityLabel:@"Slider" value:@"0" traits:UIAccessibilityTraitNone];
    [tester setValue:5 forSliderWithAccessibilityLabel:@"Slider"];
    [tester waitForViewWithAccessibilityLabel:@"Slider" value:@"5" traits:UIAccessibilityTraitNone];
}

选取系统的图片

- (void)testPickingAPhoto{
    [tester tapViewWithAccessibilityLabel:@"Photos"];
    [tester acknowledgeSystemAlert];
    [tester waitForTimeInterval:0.5f]; // Wait for view to stabilize
    [tester choosePhotoInAlbum:@"Camera Roll" atRow:1 column:2];
    [tester waitForViewWithAccessibilityLabel:@"UIImage"];
}

Show/Hide

一直寻找区域内标记为B的值为BB的可点击的按钮

- (void)testWaitingForViewWithValue{
    NSLog(@"testWaitingForViewWithValue");
    [tester waitForTappableViewWithAccessibilityLabel:@"B" value:@"BB" traits:UIAccessibilityTraitButton];
}

选择视图内可点击切换状态的按钮

- (void)testTappingOnlyIfNotSelected{
    [tester tapViewIfNotSelected:@"A"];
    [tester waitForViewWithAccessibilityLabel:@"A" traits:UIAccessibilityTraitSelected];
    
    // This should not deselect the element.
    [tester tapViewIfNotSelected:@"A"];
    [tester waitForViewWithAccessibilityLabel:@"A" traits:UIAccessibilityTraitSelected];
}
- (void)tapViewIfNotSelected:(NSString *)label{
    UIAccessibilityElement *element;
    UIView *view;
    [self waitForAccessibilityElement:&element view:&view withLabel:label value:nil traits:UIAccessibilityTraitNone tappable:YES];
    if ((element.accessibilityTraits & UIAccessibilityTraitSelected) == UIAccessibilityTraitNone) {
        [self tapAccessibilityElement:element inView:view];
    }
}

Gesture


手势操作

滑动

四个方向都可操作

typedef NS_ENUM(NSUInteger, KIFSwipeDirection) {
    KIFSwipeDirectionRight,
    KIFSwipeDirectionLeft,
    KIFSwipeDirectionUp,
    KIFSwipeDirectionDown
};

示例

- (void)testSwipingRight{
    [tester swipeViewWithAccessibilityLabel:@"Swipe Me" inDirection:KIFSwipeDirectionRight];
    [tester waitForViewWithAccessibilityLabel:@"Right"];
}

点击

- (void)testPanningRight{
    NSString* regexPattern = kPanRightRegex;
    NSPredicate *resultTestPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regexPattern];
    NSPredicate *noVelocityPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", KPanNoVelocityValue];
    
    UIView* velocityResultView = [tester waitForViewWithAccessibilityLabel:kVelocityValueLabelAccessibilityString];
    XCTAssertTrue([velocityResultView isKindOfClass:[UILabel class]], @"Found view is not a UILabel instance!");
    UILabel* velocityLabel = (UILabel*)velocityResultView;
    
    UIView* panLabel = [tester waitForTappableViewWithAccessibilityLabel:kPanMeAccessibilityString];
    CGPoint centerInView = CGPointMake(panLabel.frame.size.width / 2.0, panLabel.frame.size.height / 2.0);
    
    [panLabel dragFromPoint:centerInView toPoint:CGPointMake(centerInView.x + 30, centerInView.y)];
    XCTAssertFalse([noVelocityPredicate evaluateWithObject:velocityLabel.text], @"No valocity value found!");
    XCTAssertTrue([resultTestPredicate evaluateWithObject:velocityLabel.text], @"The result doesn`t match the %@ regex pattern", regexPattern);
}

拖拽

- (void)testScrolling{
    // Needs to be offset from the edge to prevent the navigation controller's interactivePopGestureRecognizer from triggering
    [tester scrollViewWithAccessibilityIdentifier:@"Scroll View" byFractionOfSizeHorizontal:-0.80 vertical:-0.80];
    [tester waitForTappableViewWithAccessibilityLabel:@"Bottom Right"];
    [tester scrollViewWithAccessibilityIdentifier:@"Scroll View" byFractionOfSizeHorizontal:0.80 vertical:0.80];
    [tester waitForTappableViewWithAccessibilityLabel:@"Top Left"];
}

TableView

tableView的手势滑动

 [tester swipeRowAtIndexPath:firstCellPath inTableView:tableView inDirection:KIFSwipeDirectionLeft];

点击tableview上指定的某个cell

- (void)testTappingRows{
    [tester tapRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:2] inTableViewWithAccessibilityIdentifier:@"TableView Tests Table"];
    [tester waitForViewWithAccessibilityLabel:@"Last Cell" traits:UIAccessibilityTraitSelected];
    [tester tapRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] inTableViewWithAccessibilityIdentifier:@"TableView Tests Table"];
    [tester waitForViewWithAccessibilityLabel:@"First Cell" traits:UIAccessibilityTraitSelected];
}

移动

- (void)testMoveRowUpUsingNegativeRowIndexes{
    [tester moveRowAtIndexPath:[NSIndexPath indexPathForRow:-1 inSection:1]
 toIndexPath:[NSIndexPath indexPathForRow:-3 inSection:1] inTableViewWithAccessibilityIdentifier:@"TableView Tests Table"];
}

删除

///删除第一个cell
- (void)testSwipingRows {
    
    UITableView *tableView;
    [tester waitForAccessibilityElement:NULL view:&tableView withIdentifier:@"TableView Tests Table" tappable:NO];
    [tester waitForAnimationsToFinish];
    // First row
    NSIndexPath *firstCellPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [tester swipeRowAtIndexPath:firstCellPath inTableView:tableView inDirection:KIFSwipeDirectionLeft];
    [tester waitForDeleteStateForCellAtIndexPath:firstCellPath inTableView:tableView];
    [tester tapViewWithAccessibilityLabel:@"Delete"];
    
    __KIFAssertEqualObjects([tester waitForCellAtIndexPath:firstCellPath inTableViewWithAccessibilityIdentifier:@"TableView Tests Table"].textLabel.text, @"Deleted", @"");
}

设置Button状态

- (void)testTogglingSwitch{
    [tester setOn:NO forSwitchWithAccessibilityLabel:@"Table View Switch"];
    [tester setOn:YES forSwitchWithAccessibilityLabel:@"Table View Switch"];
}

检查元素是否在视图上 当元素在视图上消失时 执行操作

- (void)testButtonAbsentAfterRemoveFromSuperview{
    UIView *view = [tester waitForViewWithAccessibilityLabel:@"Button"];
    [view removeFromSuperview];
    [tester waitForAbsenceOfViewWithAccessibilityLabel:@"Button"];
}

Picker

时间选择器

- (void)testSelectingDateInPast{
    [tester tapViewWithAccessibilityLabel:@"Date Selection"];
    NSArray *date = @[@"June", @"17", @"1965"];
    // If the UIDatePicker LocaleIdentifier would be de_DE then the date to set
    // would look like this: NSArray *date = @[@"17.", @"Juni", @"1965"
    [tester selectDatePickerValue:date];
    [tester waitForViewWithAccessibilityLabel:@"Date Selection" value:@"Jun 17, 1965" traits:UIAccessibilityTraitNone];
}

选择器

- (void)testSelectingAPickerRow{
    [tester selectPickerViewRowWithTitle:@"Charlie"];
    NSOperatingSystemVersion iOS8 = {8, 0, 0};
   [tester waitForViewWithAccessibilityLabel:@"Call Sign" value:@"Charlie" traits:UIAccessibilityTraitNone];
}

ModalView

点击alertView上的选项 取消

- (void)testInteractionWithAnAlertView{
    [tester tapViewWithAccessibilityLabel:@"UIAlertView"];
    [tester waitForViewWithAccessibilityLabel:@"Alert View"];
    [tester waitForViewWithAccessibilityLabel:@"Message"];
    [tester waitForTappableViewWithAccessibilityLabel:@"Cancel"];
    [tester waitForTappableViewWithAccessibilityLabel:@"Continue"];
    [tester tapViewWithAccessibilityLabel:@"Continue"];
    [tester waitForAbsenceOfViewWithAccessibilityLabel:@"Message"];
}

点击底部弹窗

- (void)testInteractionWithAnActionSheet{
    [tester tapViewWithAccessibilityLabel:@"UIActionSheet"];
    [tester waitForViewWithAccessibilityLabel:@"Action Sheet"];
    [tester waitForTappableViewWithAccessibilityLabel:@"Destroy"];
    [tester waitForTappableViewWithAccessibilityLabel:@"A"];
    [tester waitForTappableViewWithAccessibilityLabel:@"B"];
}

点击获取系统的activity

- (void)testInteractionWithAnActivityViewController{
    if (!NSClassFromString(@"UIActivityViewController")) {
        return;
    }
    [tester tapViewWithAccessibilityLabel:@"UIActivityViewController"];
    [tester waitForTappableViewWithAccessibilityLabel:@"Copy"];
    [tester waitForTappableViewWithAccessibilityLabel:@"Mail"];
}

CollectionView

点击Item

- (void)testTappingItems{
    [tester tapItemAtIndexPath:[NSIndexPath indexPathForItem:199 inSection:0] inCollectionViewWithAccessibilityIdentifier:@"CollectionView Tests CollectionView"];
    [tester waitForViewWithAccessibilityLabel:@"Last Cell" traits:UIAccessibilityTraitSelected];
    [tester tapItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] inCollectionViewWithAccessibilityIdentifier:@"CollectionView Tests CollectionView"];
    [tester waitForViewWithAccessibilityLabel:@"First Cell" traits:UIAccessibilityTraitSelected];
}

ScrollView

滑动

///down up right left 为ScrollView上的控件
- (void)testScrollingToTapOffscreenViews{
    [tester tapViewWithAccessibilityLabel:@"Down"];
    [tester tapViewWithAccessibilityLabel:@"Up"];
    [tester tapViewWithAccessibilityLabel:@"Right"];
    [tester tapViewWithAccessibilityLabel:@"Left"];
}

Landscape

切换横屏

- (void)beforeAll{
    [system simulateDeviceRotationToOrientation:UIDeviceOrientationLandscapeLeft];
    [tester scrollViewWithAccessibilityIdentifier:@"Test Suite TableView" byFractionOfSizeHorizontal:0 vertical:-0.2];
}
- (void)afterAll{
    [system simulateDeviceRotationToOrientation:UIDeviceOrientationPortrait];
    [tester waitForTimeInterval:0.5];
}

TextField

textField输入文字

登录注册时常常使用这种方式进行行为描述

- (void)testWaitingForSearchFieldToBecomeFirstResponder{
    [tester tapViewWithAccessibilityLabel:nil traits:UIAccessibilityTraitSearchField];
    [tester waitForFirstResponderWithAccessibilityLabel:nil traits:UIAccessibilityTraitSearchField];
    [tester enterTextIntoCurrentFirstResponder:@"text"];
    [tester waitForViewWithAccessibilityLabel:nil value:@"text" traits:UIAccessibilityTraitSearchField];
}

WebView

点击链接

- (void)testTappingLinks {
    [tester tapViewWithAccessibilityLabel:@"A link"];
    [tester waitForViewWithAccessibilityLabel:@"Page 2"];
}

输入文本

- (void)testEnteringText {
    [tester tapViewWithAccessibilityLabel:@"Input Label"];
    [tester enterTextIntoCurrentFirstResponder:@"Keyboard text"];
}

滚动Scroll

- (void)testScrolling {
    // Off screen, the web view will need to be scrolled down
    [tester waitForViewWithAccessibilityLabel:@"Footer"];
}

Background

进入后台操作

- (void)testBackgroundApp {
    [tester waitForViewWithAccessibilityLabel:@"Start"];
    [system deactivateAppForDuration:5];
    [tester waitForViewWithAccessibilityLabel:@"Back"];
}

PullToRefesh

下拉刷新

-(void) testPullToRefreshByAccessibilityLabelWithDuration{
    UITableView *tableView;
    [tester waitForAccessibilityElement:NULL view:&tableView withIdentifier:@"Test Suite TableView" tappable:NO];

    [tester pullToRefreshViewWithAccessibilityLabel:@"Table View" pullDownDuration:KIFPullToRefreshInAboutThreeSeconds];
    [tester waitForViewWithAccessibilityLabel:@"Bingo!"];
    [tester waitForAbsenceOfViewWithAccessibilityLabel:@"Bingo!"];

    [tester waitForTimeInterval:5.0f]; //make sure the PTR is finished.
}

Typing

复制粘贴

- (void)testEnteringTextIntoViewWithAccessibilityLabel{
    [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" duration:2];
    [tester tapViewWithAccessibilityLabel:@"Select All"];
    [tester tapViewWithAccessibilityLabel:@"Cut"];
    [tester enterText:@"Yo" intoViewWithAccessibilityLabel:@"Greeting"];
    [tester waitForViewWithAccessibilityLabel:@"Greeting" value:@"Yo" traits:UIAccessibilityTraitNone];
}

清除

- (void)testClearingAndEnteringTextIntoViewWithAccessibilityLabel
{
    [tester clearTextFromAndThenEnterText:@"Yo" intoViewWithAccessibilityLabel:@"Greeting"];
}

两个textField联动

- (void)testEnteringReturnCharacterIntoViewWithAccessibilityLabel
{
    [tester enterText:@"Hello\n" intoViewWithAccessibilityLabel:@"Other Text"];
    [tester waitForFirstResponderWithAccessibilityLabel:@"Greeting"];
    [tester enterText:@", world\n" intoViewWithAccessibilityLabel:@"Greeting" traits:UIAccessibilityTraitNone expectedResult:@"Hello, world"];
}

输入Emoj表情

- (void)testEnteringEmojiCharactersIntoViewWithAccessibilityLabel{
    NSString *text = @" 😓He😤ll👿o";
    [tester clearTextFromAndThenEnterText:text intoViewWithAccessibilityLabel:@"Greeting"];
    UITextField * tf = (UITextField*)[tester waitForViewWithAccessibilityLabel:@"Greeting"];
    XCTAssertTrue([tf.text isEqualToString:text]);
}

预期TextField的输入

- (void)testThatBackspaceDeletesOneCharacter{
    [tester enterText:@"hi\bello" intoViewWithAccessibilityLabel:@"Other Text" traits:UIAccessibilityTraitNone expectedResult:@"hello"];
    [tester waitForViewWithAccessibilityLabel:@"Greeting" value:@"Deleted something." traits:UIAccessibilityTraitNone];
    UIView *textView = [tester waitForViewWithAccessibilityLabel:@"Other Text"];
    XCTAssertEqualObjects([tester textFromView:textView], @"hello");
}

CascadingFailure

失败用例测试

- (void)testCascadingFailure{
    KIFExpectFailure([system failA]);
    KIFExpectFailureWithCount([system failA], 4);
}

补充Tips

按钮的title、类的title,可以直接做为访问标签
如果UI组件被键盘挡住了,需要先退掉键盘
如果UI组件不在屏幕范围内,不可以访问,但是滚动视图,可以访问,且会出现在可视范围。
无法访问系统自己的弹窗。例如app想定位用户,不能自动点击允许;但是app自己的弹窗,可以操作的
类内部的多个测试方法的测试顺序,是无序的
类与类的测试顺序,是无序的
可以将某个测试类或者测试方法给disable掉

最后

到目前为止,我们应该对KIF的使用性有很好的了解,脑子里也应该有不少主意,大概了解如何利用这个高效的功能测试工具来测试你自己的应用程序。由于KIF测试用例是OCUnit的子类,并在标准的Xcode5测试框架下运行,你可以使用持续集成来跑这些测试。当你干别的事情的时候,你拥有了一个能够像人的手指一样触控的机器人去测试你的应用程序。

参考文献:

推荐阅读

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 前言 如果有测试大佬发现内容不对,欢迎指正,我会及时修改。 大多数的iOS App(没有持续集成)迭代流程是这样的...
    默默_David阅读 1,660评论 0 4
  • 在苹果电脑的屏幕下方,有一个功能类似Windows任务栏的东西。可以显示、切换正在运行的程序,也可以点击上面程序的...
    大鱼小贱阅读 1,998评论 1 0
  • 天啊,天啊,我真的是白痴啊!居然敢这样跟上司讲话,你说我会不会被炒鱿鱼啊!我回过头来对着老板笑了一下。老板忽然...
    祺艺果阅读 179评论 0 0
  • 成功学就是打鸡血。成功学就是兴奋不已的表情、慷慨激昂的演讲、整齐划一的动作。成功学就是我一定要!我一定行!我是最棒...
    开启2017阅读 193评论 2 6