最近科技公司流年不利,那边与整个硅谷唱反调的川普逆袭上台了,这边特斯拉被评为美国最不可靠汽车品牌,据报道是因为特斯拉为Model X增加了过于复杂的功能(高科技多也怪我咯),如前门采用电动开启方式,中排座椅实现了电动移动,所有这些功能整合在一个平台上,导致可靠性下滑。通俗解释下就是电动门有个小bug,电动座椅又有个小bug,一堆小bug最终导致的大bug,人命关天了,本篇就来谈谈软件开发中避免小bug的技术:单元测试。
本文将介绍以下内容:
1、iOS开发中添加单元测试的方法。
2、如何写单元测试用例及用例组。
3、介绍单元测试的一些基础概念。
本篇作为重构的例子(想了解重构是什么,另参见他们总在说重构,不过是重写 ),假设了一个视频网站的电影点播系统,每次点击播放就会收取费用,按电影种类不同,时段不同,则收费不同,最终计算出顾客的总消费,并计算积分。这个例子的类关系比较清晰易懂,用OC语言实现,iOS开发的童鞋看起来会比较亲切,心急的童鞋可以跳过源码部分,先看后面添加单元测试的部分准备测试工具,需要了解细节时再回头看源码。
系统包含一个电影类,顾客类,及点播类,类关系如下图所示:
类关系图.png
电影类
// // Movie.h // RefactorDemo // // Created by xishi on 16/10/29. // Copyright ? 2016年 xs. All rights reserved. // typedef NS_ENUM(NSUInteger, MovieEnum) { MovieEnumChildrens = 2, MovieEnumRegular = 0, MovieEnumNewRelease = 1 }; @class Movie; @interface Movie : NSObject @property(nonatomic, copy) NSString *title; @property(nonatomic) int priceCode; - (id)initWithTitle:(NSString *)title priceCode:(int)priceCode; @end // // Movie.m // RefactorDemo // // Created by xishi on 16/10/29. // Copyright ? 2016年 xs. All rights reserved. // #import "Movie.h" @implementation Movie - (id)initWithTitle:(NSString *)title priceCode:(int)priceCode { self = [super init]; if (self) { _title = title; _priceCode = priceCode; } return self; } @end |
点播类:
点播类定义了点播行为,关心点播了什么电影,及点播的时段,这些都影响最终收取的费用。
// // Demand.h // RefactorDemo // // Created by xishi on 16/10/29. // Copyright ? 2016年 xs. All rights reserved. // #import <Foundation/Foundation.h> typedef NS_ENUM(NSUInteger, TimePeriodEnum) { TimePeriodEnumWorkDaytime = 1, TimePeriodEnumWorkNight = 2, TimePeriodEnumWeekend = 3 }; @class Movie; @interface Demand : NSObject @property(nonatomic) Movie *movie; @property(nonatomic, assign) int timePeriod; - (id)initWithMovie:(Movie *)movie timePeriod:(TimePeriodEnum)timePeriod; @end // // Demand.m // RefactorDemo // // Created by xishi on 16/10/29. // Copyright ? 2016年 xs. All rights reserved. // #import "Demand.h" #import "Movie.h" @implementation Demand - (id)initWithMovie:(Movie *)movie timePeriod:(TimePeriodEnum)timePeriod { self = [super init]; if (self) { _movie = movie; _timePeriod = timePeriod; } return self; } @end |
顾客类
// // Customer.h // RefactorDemo // // Created by xishi on 16/10/29. // Copyright ? 2016年 xs. All rights reserved. // #import <Foundation/Foundation.h> @class Demand; @interface Customer : NSObject - (id)initCustomerWithName:(NSString *)name; - (void)addDemand:(Demand *)demand; - (NSString *)statement; @end // // Customer.m // RefactorDemo // // Created by xishi on 16/10/29. // Copyright ? 2016年 xs. All rights reserved. // #import "Customer.h" #import "Demand.h" #import "Movie.h" @interface Customer () { NSString *_name; NSMutableArray *_demands; } @end @implementation Customer - (id)initCustomerWithName:(NSString *)name { self = [super init]; if (self) { _name = name; } return self; } - (void)addDemand:(Demand *)demand { if (!_demands) { _demands = [[NSMutableArray alloc] init]; } [_demands addObject:demand]; } - (NSString *)statement { double totalAmount = 0; int frequentDemandPotnts = 0; NSMutableString *result = [NSMutableString stringWithFormat:@"%@的点播清单\\\\n", _name]; for (Demand *aDemand in _demands) { double thisAmount = 0; // 根据不同电影定价: switch (aDemand.movie.priceCode) { case MovieEnumRegular: thisAmount += 2; // 普通电影2元一次 break; case MovieEnumNewRelease: thisAmount += 3; // 新电影3元一次 break; case MovieEnumChildrens: thisAmount += 1.5; // 儿童电影1.5元一次 } // 根据不同时段定价: if (aDemand.timePeriod == TimePeriodEnumWorkDaytime) thisAmount *= 1.0; // 工作日全价 else if (aDemand.timePeriod == TimePeriodEnumWeekend) { thisAmount *= 0.5; // 周末半价 } else if (aDemand.timePeriod == TimePeriodEnumWorkNight){ thisAmount *= 1.5; // 下班1.5倍 } frequentDemandPotnts++; // 周末点播新片积分翻倍: if ((aDemand.movie.priceCode == MovieEnumNewRelease) && aDemand.timePeriod == TimePeriodEnumWeekend) { frequentDemandPotnts++; } [result appendFormat:@"\\\\t%@\\\\t%@ 元\\\\n", aDemand.movie.title, @(thisAmount)]; totalAmount += thisAmount; } [result appendFormat:@"费用总计 %@ 元\\\\n", @(totalAmount).stringValue]; [result appendFormat:@"获得积分 %@", @(frequentDemandPotnts).stringValue]; return result; } @end |