自动化Test使用详细解析(一) —— 基本使用(一)

版本记录

版本号 时间
V1.0 2017.10.10

前言

UI Tests是一个自动测试UI与交互的Testing组件,它可以通过编写代码、或者是记录开发者的操作过程并代码化,来实现自动点击某个按钮、视图,或者自动输入文字等功能。接下来几篇我们就说一下该技术的使用。

基本介绍

大家多UI Tests并不陌生,每当我们新建立一个工程,在建立工程界面我们都可以看到包含UITest的复选框。如下图所示。

这里大家可以看到,下面两个复选框,红色的是数据存储Core Data,下面绿色的框就是我们要说的UI Test。相信大家对它都似曾相识了吧,只是一般我们不用而已。


使用方法

1. 添加UI Tests

新建工程时添加

添加UI Tests有两个方法,第一种就是如上图所示,在创建工程的时候就勾上复选框支持UI Tests,勾选后新建立的工程如下所示。

这里可以看见,项目中自动生成了文件夹JJUITest_demo1UITests,打开JJUITest_demo1UITests.m我们就可以看见里面的代码。如下所示。

#import <XCTest/XCTest.h>

@interface JJUITest_demo1UITests : XCTestCase

@end

@implementation JJUITest_demo1UITests

- (void)setUp {
    [super setUp];
    
    // Put setup code here. This method is called before the invocation of each test method in the class.
    
    // In UI tests it is usually best to stop immediately when a failure occurs.
    self.continueAfterFailure = NO;
    // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
    [[[XCUIApplication alloc] init] launch];
    
    // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];
}

- (void)testExample {
    // Use recording to get started writing UI tests.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}

@end

这里面测试代码写在- (void)testExample里面,后面我们会讲解。

后续为工程添加

有的时候我们还有这样的需求,那就是,我们开始的时候并没打算使用UI Tests,后来想使用,那这样我们怎么做呢?别急,我们有办法,可以为后续为以前建立的工程添加UI Tests。我们先建立一个不勾选复选框的工程。如下图所示。

下面我们就看一下如何为这个工程添加UI Tests

  • 选择File -> New -> Target
  • 选择iOS UI Testing Bundle
  • 可以起个名字,我这里就用系统给生成的了
  • 这里就是我们添加UI Tests 后的结果。

2. 添加测试代码

我们前面提到过,我们可以在方法- (void)testExample中添加测试代码。对于新手可能不会写代码,这里苹果为我们先到了,可以自动生成代码,也可以手动写入代码。下面我们就看一下这两种方法。

准备页面和素材

为了测试,我们先创建一个页面。代码如下所示。

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor lightGrayColor];
    
    UIButton *demoButton = [[UIButton alloc] initWithFrame:CGRectMake(50.0, 200.0, 200.0, 100.0)];
    demoButton.backgroundColor = [UIColor redColor];
    [demoButton addTarget:self action:@selector(buttonDidClick) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:demoButton];
}

#pragma mark - Object Private Function

- (void)buttonDidClick
{
    CGFloat colorRandomRedsValue = arc4random()%255;
    CGFloat colorRandomBluesValue = arc4random()%255;
    CGFloat colorRandomGreenValue = arc4random()%255;
    self.view.backgroundColor = [UIColor colorWithRed:colorRandomRedsValue/255.0 green:colorRandomGreenValue/255.0 blue:colorRandomBluesValue/255.0 alpha:1.0];
}

@end

这个是页面操作效果。

自动生成代码

如何自动生成代码呢?我们需要选择测试文件,并点击左下角的红色按钮,这时候开始进行操作,它会记录你的操作步骤,并生成测试代码。

大家可以看见,是暗红色不可点击的,这个时候我们把鼠标插入点放在方法- (void)testExample里面,这个时候按钮就会变得深红色就是可以点击的了。这个时候我们点击按钮,然后开始操作,系统就会记录我们的操作并生成代码。

大家可以看一下,下面就是我生成的代码。

- (void)testExample
{
    XCUIElement *window = [[[[XCUIApplication alloc] init] childrenMatchingType:XCUIElementTypeWindow] elementBoundByIndex:0];
    [[window childrenMatchingType:XCUIElementTypeOther].element tap];
    
    XCUIElement *button = [window.otherElements childrenMatchingType:XCUIElementTypeButton].element;
    [button tap];
    [button tap];
    [button tap];
    [button tap];
}

@end

这里我一共点击了四次按钮。

下面我们点击下面这个开关或者入口就可以自动测试了。

下面是控制台打印出的部分信息。

当测试成功或者失败都会弹出一个提示框,存在一段时间就会自动消失。

手动输入代码

前面说的自动生成代码,是xcode替我们做了很多的事情,这里示例比较简单,但是对于一些大型项目来说,有时候生成的测试代码并不一定很准确,所以很多时候我们需要手动的编写测试代码。但是对于初学者来说,并不是很简单,需要对相关语法有一定的了解,下面我们就先看一下想关语法和知识。

XCUIApplication

XCUIApplication这个类继承自XCUIElement,掌管应用程序的生命周期。它有下面几个方法和属性。

@interface XCUIApplication : XCUIElement

- (instancetype)init NS_DESIGNATED_INITIALIZER;
+ (instancetype)new;
- (instancetype)initWithBundleIdentifier:(NSString *)bundleIdentifier
- (void)launch;
- (void)activate;
- (void)terminate;
- (BOOL)waitForState:(XCUIApplicationState)state timeout:(NSTimeInterval)timeout XCT_WARN_UNUSED;

@property (nonatomic, copy) NSArray <NSString *> *launchArguments;
@property (nonatomic, copy) NSDictionary <NSString *, NSString *> *launchEnvironment;
@property (readonly) XCUIApplicationState state;

XCUIElement

XCUIElement继承NSObject,实现协议XCUIElementAttributes, XCUIElementTypeQueryProvider可以表示系统的各种UI元素。

@interface XCUIElement : NSObject <XCUIElementAttributes, XCUIElementTypeQueryProvider>

+ (instancetype)new XCT_UNAVAILABLE("Use XCUIElementQuery to create XCUIElement instances.");
- (instancetype)init XCT_UNAVAILABLE("Use XCUIElementQuery to create XCUIElement instances.");

/*! Test to determine if the element exists. */
@property (readonly) BOOL exists;

/*! Waits the specified amount of time for the element's exist property to be true and returns false if the timeout expires without the element coming into existence. */
- (BOOL)waitForExistenceWithTimeout:(NSTimeInterval)timeout XCT_WARN_UNUSED;

/*! Whether or not a hit point can be computed for the element for the purpose of synthesizing events. */
@property (readonly, getter = isHittable) BOOL hittable;

/*! Returns a query for all descendants of the element matching the specified type. */
- (XCUIElementQuery *)descendantsMatchingType:(XCUIElementType)type;

/*! Returns a query for direct children of the element matching the specified type. */
- (XCUIElementQuery *)childrenMatchingType:(XCUIElementType)type;

#if !TARGET_OS_TV
/*! Creates and returns a new coordinate that will compute its screen point by adding the offset multiplied by the size of the element’s frame to the origin of the element’s frame. */
- (XCUICoordinate *)coordinateWithNormalizedOffset:(CGVector)normalizedOffset;
#endif

/*!
 @discussion
 Provides debugging information about the element. The data in the string will vary based on the
 time at which it is captured, but it may include any of the following as well as additional data:
    • Values for the elements attributes.
    • The entire tree of descendants rooted at the element.
    • The element's query.
 This data should be used for debugging only - depending on any of the data as part of a test is unsupported.
 */
@property (readonly, copy) NSString *debugDescription;

@end

下面重点说几个方法

/*! Returns a query for all descendants of the element matching the specified type. */
- (XCUIElementQuery *)descendantsMatchingType:(XCUIElementType)type;

/*! Returns a query for direct children of the element matching the specified type. */
- (XCUIElementQuery *)childrenMatchingType:(XCUIElementType)type;

方法descendantsMatchingType :和方法childrenMatchingType都是返回的是子类,区别在于前者取某种类型的元素以及它的子类集合,比如自定义按钮,后者取某种类型的元素集合,不包含它的子类,比如系统级别的UIButton。

还有属性@property (readonly) BOOL exists;用来判断该控件是否存在,不存在的控件如果向它发送消息,会抛出异常。

下面在看一下相关的交互方法。

tap():         点击
doubleTap():   双击

//轻扫
/*!
 * Sends a swipe-up gesture.
 */
- (void)swipeUp;

/*!
 * Sends a swipe-down gesture.
 */
- (void)swipeDown;

/*!
 * Sends a swipe-left gesture.
 */
- (void)swipeLeft;

/*!
 * Sends a swipe-right gesture.
 */
- (void)swipeRight;

pressForDuration(duration: NSTimeInterval):  //长按
typeText(text: String):  用于textField和textView输入文本时使用,使用前要确保文本框获得输入焦点,可以使用tap()函数使其获得焦点

//捏合
- (void)pinchWithScale:(CGFloat)scale velocity:(CGFloat)velocity;

//旋转
- (void)rotate:(CGFloat)rotation withVelocity:(CGFloat)velocity;

//点击次数
- (void)tapWithNumberOfTaps:(NSUInteger)numberOfTaps numberOfTouches:(NSUInteger)numberOfTouches;
  • 下面的就是XCUIElement的一个分类XCUIScreenshotProviding
@interface XCUIElement (XCUIScreenshotProviding) <XCUIScreenshotProviding>
@end
  • 还有分类XCUIElementKeyboardEvents
@interface XCUIElement (XCUIElementKeyboardEvents)

/*!
 * Types a string into the element. The element or a descendant must have keyboard focus; otherwise an
 * error is raised.
 *
 * This API discards any modifiers set in the current context by +performWithKeyModifiers:block: so that
 * it strictly interprets the provided text. To input keys with modifier flags, use  -typeKey:modifierFlags:.
 */
- (void)typeText:(NSString *)text;

#if TARGET_OS_OSX

/*!
 * Hold modifier keys while the given block runs. This method pushes and pops the modifiers as global state
 * without need for reference to a particular element. Inside the block, elements can be clicked on, dragged
 * from, typed into, etc.
 */
+ (void)performWithKeyModifiers:(XCUIKeyModifierFlags)flags block:(XCT_NOESCAPE void (^)(void))block;

/*!
 * Types a single key with the specified modifier flags. Although `key` is a string, it must represent
 * a single key on a physical keyboard; strings that resolve to multiple keys will raise an error at runtime.
 * In addition to literal string key representations like "a", "6", and "[", keys such as the arrow keys,
 * command, control, option, and function keys can be typed using constants defined for them in XCUIKeyboardKeys.h
 */
- (void)typeKey:(NSString *)key modifierFlags:(XCUIKeyModifierFlags)flags;

#endif // TARGET_OS_OSX

@end
  • 还有分类XCUIElementTouchEvents
@interface XCUIElement (XCUIElementTouchEvents)

/*!
 * Sends a tap event to a hittable point computed for the element.
 */
- (void)tap;

/*!
 * Sends a double tap event to a hittable point computed for the element.
 */
- (void)doubleTap;

/*!
 * Sends a two finger tap event to a hittable point computed for the element.
 */
- (void)twoFingerTap;

/*!
 * Sends one or more taps with one of more touch points.
 *
 * @param numberOfTaps
 * The number of taps.
 *
 * @param numberOfTouches
 * The number of touch points.
 */
- (void)tapWithNumberOfTaps:(NSUInteger)numberOfTaps numberOfTouches:(NSUInteger)numberOfTouches;

/*!
 * Sends a long press gesture to a hittable point computed for the element, holding for the specified duration.
 *
 * @param duration
 * Duration in seconds.
 */
- (void)pressForDuration:(NSTimeInterval)duration;

/*!
 * Initiates a press-and-hold gesture that then drags to another element, suitable for table cell reordering and similar operations.
 * @param duration
 * Duration of the initial press-and-hold.
 * @param otherElement
 * The element to finish the drag gesture over. In the example of table cell reordering, this would be the reorder element of the destination row.
 */
- (void)pressForDuration:(NSTimeInterval)duration thenDragToElement:(XCUIElement *)otherElement;

/*!
 * Sends a swipe-up gesture.
 */
- (void)swipeUp;

/*!
 * Sends a swipe-down gesture.
 */
- (void)swipeDown;

/*!
 * Sends a swipe-left gesture.
 */
- (void)swipeLeft;

/*!
 * Sends a swipe-right gesture.
 */
- (void)swipeRight;

/*!
 * Sends a pinching gesture with two touches.
 *
 * The system makes a best effort to synthesize the requested scale and velocity: absolute accuracy is not guaranteed.
 * Some values may not be possible based on the size of the element's frame - these will result in test failures.
 *
 * @param scale
 * The scale of the pinch gesture.  Use a scale between 0 and 1 to "pinch close" or zoom out and a scale greater than 1 to "pinch open" or zoom in.
 *
 * @param velocity
 * The velocity of the pinch in scale factor per second.
 */
- (void)pinchWithScale:(CGFloat)scale velocity:(CGFloat)velocity;

/*!
 * Sends a rotation gesture with two touches.
 *
 * The system makes a best effort to synthesize the requested rotation and velocity: absolute accuracy is not guaranteed.
 * Some values may not be possible based on the size of the element's frame - these will result in test failures.
 *
 * @param rotation
 * The rotation of the gesture in radians.
 *
 * @param velocity
 * The velocity of the rotation gesture in radians per second.
 */
- (void)rotate:(CGFloat)rotation withVelocity:(CGFloat)velocity;

@end
  • 还有分类XCUIElementMouseEvents
@interface XCUIElement (XCUIElementMouseEvents)

/*!
 * Moves the cursor over the element.
 */
- (void)hover;

/*!
 * Sends a click event to a hittable point computed for the element.
 */
- (void)click;

/*!
 * Sends a double click event to a hittable point computed for the element.
 */
- (void)doubleClick;

/*!
 * Sends a right click event to a hittable point computed for the element.
 */
- (void)rightClick;

/*!
 * Clicks and holds for a specified duration (generally long enough to start a drag operation) then drags
 * to the other element.
 */
- (void)clickForDuration:(NSTimeInterval)duration thenDragToElement:(XCUIElement *)otherElement;

/*!
 * Scroll the view the specified pixels, x and y.
 */
- (void)scrollByDeltaX:(CGFloat)deltaX deltaY:(CGFloat)deltaY;

@end
  • 还有分类XCUIElementTouchBarEvents
@interface XCUIElement (XCUIElementTouchBarEvents)

/*!
 * Sends a tap event to a central point computed for the element.
 */
- (void)tap;

/*!
 * Sends a double tap event to a central point computed for the element.
 */
- (void)doubleTap;

/*!
 * Sends a long press gesture to a central point computed for the element, holding for the specified duration.
 *
 * @param duration
 * Duration in seconds.
 */
- (void)pressForDuration:(NSTimeInterval)duration;

/*!
 * Initiates a press-and-hold gesture that then drags to another element.
 * @param duration
 * Duration of the initial press-and-hold.
 * @param otherElement
 * The element to finish the drag gesture over.
 */
- (void)pressForDuration:(NSTimeInterval)duration thenDragToElement:(XCUIElement *)otherElement;

@end
  • 还有分类XCUIElementTypeSlider
/*! This category on XCUIElement provides functionality for automating UISlider and NSSlider. */
@interface XCUIElement (XCUIElementTypeSlider)

/*! Manipulates the UI to change the displayed value of the slider to one based on a normalized position. 0 corresponds to the minimum value of the slider, 1 corresponds to its maximum value. The adjustment is a "best effort" to move the indicator to the desired position; absolute fidelity is not guaranteed. */
- (void)adjustToNormalizedSliderPosition:(CGFloat)normalizedSliderPosition;

/*! Returns the position of the slider's indicator as a normalized value where 0 corresponds to the minimum value of the slider and 1 corresponds to its maximum value. */
@property (readonly) CGFloat normalizedSliderPosition;

@end
  • 还有分类XCUIElementTypePickerWheel
/*! This category on XCUIElement provides functionality for automating the picker wheels of UIPickerViews and UIDatePickers. */
@interface XCUIElement (XCUIElementTypePickerWheel)

/*! Changes the displayed value for the picker wheel. Will generate a test failure if the specified value is not available. */
- (void)adjustToPickerWheelValue:(NSString *)pickerWheelValue;

@end

大家也注意到了XCUIElement遵守了两个协议,XCUIElementAttributes, XCUIElementTypeQueryProvider

XCUIElementAttributes

描述在用户界面元素上公开的属性并在查询匹配期间可用的协议。 这些属性表示暴露给系统的数据。

*! Protocol describing the attributes exposed on user interface elements and available during query matching. These attributes represent data exposed to the Accessibility system. */
@protocol XCUIElementAttributes

/*! The accessibility identifier. */
@property (readonly) NSString *identifier;

/*! The frame of the element in the screen coordinate space. */
@property (readonly) CGRect frame;

/*! The raw value attribute of the element. Depending on the element, the actual type can vary. */
@property (readonly, nullable) id value;

/*! The title attribute of the element. */
@property (readonly, copy) NSString *title;

/*! The label attribute of the element. */
@property (readonly, copy) NSString *label;

/*! The type of the element. /seealso XCUIElementType. */
@property (readonly) XCUIElementType elementType;

/*! Whether or not the element is enabled for user interaction. */
@property (readonly, getter = isEnabled) BOOL enabled;

/*! The horizontal size class of the element. */
@property (readonly) XCUIUserInterfaceSizeClass horizontalSizeClass;

/*! The vertical size class of the element. */
@property (readonly) XCUIUserInterfaceSizeClass verticalSizeClass;

/*! The value that is displayed when the element has no value. */
@property (readonly, nullable) NSString *placeholderValue;

/*! Whether or not the element is selected. */
@property (readonly, getter = isSelected) BOOL selected;

#if TARGET_OS_TV
/*! Whether or not the elment has UI focus. */
@property (readonly) BOOL hasFocus;
#endif

@end

XCUIElementTypeQueryProvider

里面包含了系统中大部分UI控件的类型,可通过读属性的方式取得某种类型的UI集合。

@protocol XCUIElementTypeQueryProvider

@property (readonly, copy) XCUIElementQuery *touchBars;
@property (readonly, copy) XCUIElementQuery *groups;
@property (readonly, copy) XCUIElementQuery *windows;
@property (readonly, copy) XCUIElementQuery *sheets;
@property (readonly, copy) XCUIElementQuery *drawers;
@property (readonly, copy) XCUIElementQuery *alerts;
@property (readonly, copy) XCUIElementQuery *dialogs;
@property (readonly, copy) XCUIElementQuery *buttons;
@property (readonly, copy) XCUIElementQuery *radioButtons;
@property (readonly, copy) XCUIElementQuery *radioGroups;
@property (readonly, copy) XCUIElementQuery *checkBoxes;
@property (readonly, copy) XCUIElementQuery *disclosureTriangles;
@property (readonly, copy) XCUIElementQuery *popUpButtons;
@property (readonly, copy) XCUIElementQuery *comboBoxes;
@property (readonly, copy) XCUIElementQuery *menuButtons;
@property (readonly, copy) XCUIElementQuery *toolbarButtons;
@property (readonly, copy) XCUIElementQuery *popovers;
@property (readonly, copy) XCUIElementQuery *keyboards;
@property (readonly, copy) XCUIElementQuery *keys;
@property (readonly, copy) XCUIElementQuery *navigationBars;
@property (readonly, copy) XCUIElementQuery *tabBars;
@property (readonly, copy) XCUIElementQuery *tabGroups;
@property (readonly, copy) XCUIElementQuery *toolbars;
@property (readonly, copy) XCUIElementQuery *statusBars;
@property (readonly, copy) XCUIElementQuery *tables;
@property (readonly, copy) XCUIElementQuery *tableRows;
@property (readonly, copy) XCUIElementQuery *tableColumns;
@property (readonly, copy) XCUIElementQuery *outlines;
@property (readonly, copy) XCUIElementQuery *outlineRows;
@property (readonly, copy) XCUIElementQuery *browsers;
@property (readonly, copy) XCUIElementQuery *collectionViews;
@property (readonly, copy) XCUIElementQuery *sliders;
@property (readonly, copy) XCUIElementQuery *pageIndicators;
@property (readonly, copy) XCUIElementQuery *progressIndicators;
@property (readonly, copy) XCUIElementQuery *activityIndicators;
@property (readonly, copy) XCUIElementQuery *segmentedControls;
@property (readonly, copy) XCUIElementQuery *pickers;
@property (readonly, copy) XCUIElementQuery *pickerWheels;
@property (readonly, copy) XCUIElementQuery *switches;
@property (readonly, copy) XCUIElementQuery *toggles;
@property (readonly, copy) XCUIElementQuery *links;
@property (readonly, copy) XCUIElementQuery *images;
@property (readonly, copy) XCUIElementQuery *icons;
@property (readonly, copy) XCUIElementQuery *searchFields;
@property (readonly, copy) XCUIElementQuery *scrollViews;
@property (readonly, copy) XCUIElementQuery *scrollBars;
@property (readonly, copy) XCUIElementQuery *staticTexts;
@property (readonly, copy) XCUIElementQuery *textFields;
@property (readonly, copy) XCUIElementQuery *secureTextFields;
@property (readonly, copy) XCUIElementQuery *datePickers;
@property (readonly, copy) XCUIElementQuery *textViews;
@property (readonly, copy) XCUIElementQuery *menus;
@property (readonly, copy) XCUIElementQuery *menuItems;
@property (readonly, copy) XCUIElementQuery *menuBars;
@property (readonly, copy) XCUIElementQuery *menuBarItems;
@property (readonly, copy) XCUIElementQuery *maps;
@property (readonly, copy) XCUIElementQuery *webViews;
@property (readonly, copy) XCUIElementQuery *steppers;
@property (readonly, copy) XCUIElementQuery *incrementArrows;
@property (readonly, copy) XCUIElementQuery *decrementArrows;
@property (readonly, copy) XCUIElementQuery *tabs;
@property (readonly, copy) XCUIElementQuery *timelines;
@property (readonly, copy) XCUIElementQuery *ratingIndicators;
@property (readonly, copy) XCUIElementQuery *valueIndicators;
@property (readonly, copy) XCUIElementQuery *splitGroups;
@property (readonly, copy) XCUIElementQuery *splitters;
@property (readonly, copy) XCUIElementQuery *relevanceIndicators;
@property (readonly, copy) XCUIElementQuery *colorWells;
@property (readonly, copy) XCUIElementQuery *helpTags;
@property (readonly, copy) XCUIElementQuery *mattes;
@property (readonly, copy) XCUIElementQuery *dockItems;
@property (readonly, copy) XCUIElementQuery *rulers;
@property (readonly, copy) XCUIElementQuery *rulerMarkers;
@property (readonly, copy) XCUIElementQuery *grids;
@property (readonly, copy) XCUIElementQuery *levelIndicators;
@property (readonly, copy) XCUIElementQuery *cells;
@property (readonly, copy) XCUIElementQuery *layoutAreas;
@property (readonly, copy) XCUIElementQuery *layoutItems;
@property (readonly, copy) XCUIElementQuery *handles;
@property (readonly, copy) XCUIElementQuery *otherElements;
@property (readonly, copy) XCUIElementQuery *statusItems;

/*!
 * Returns an element that will use the query for resolution. This changes how the query is resolved
 * at runtime; instead of evaluating against every element in the user interface, `firstMatch` stops
 * the search as soon as a single matching element is found. This can result in significantly faster
 * evaluation if the element is located early in a large view hierarchy but also means that multiple
 * matches will not be detected.
 */
@property (readonly) XCUIElement *firstMatch;

@end

NS_ASSUME_NONNULL_END

#endif

后记

未完,待续~~~

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

推荐阅读更多精彩内容

  • 编写测试可不是一项迷人的工作;然而,由于测试可以避免使你的宝贝应用程序变成一块充斥错误的大垃圾场,所以编写测试又是...
    cosWriter阅读 1,555评论 0 4
  • 原文地址 作为一个好的开发者,你会尽全力测试全部的功能和你写的代码逻辑及其结果。但是很少会把所有的逻辑和结果都测试...
    chdo002阅读 811评论 0 1
  • UI Tests是什么? UI Tests是一个自动测试UI与交互的Testing组件 UI Tests有什么用?...
    sunljz阅读 19,555评论 27 88
  • 大多数的iOS App (没有持续集成)迭代流程是这样的: 也就是说,测试是发布之前的最后一道关卡。如果bug不能...
    伯牙呀阅读 4,859评论 1 22
  • 我向来很少打电话,以前久久打一次,嫂子会说我这么久不打电话,我会有点抵触,更不想打电话。今天打了个电话,同样是久久...
    惜惜阅读 156评论 0 0