æè¿ãiOSã§ã®iFunnyã®UIãã¹ãã®å®è£
ãåãäžããŸããã ãã®éã¯åä»ã§ãé·ããå
šäœè«çã§ãã ããã§ããç§ã¯ãã®æ¹åãžã®ç§ã®æåã®ã¹ããããè³¢ã人ã
ãšå
±æããããšæã£ãŠããŸãã ç§ãã¡ã¯çå®ã§ãããµããããŸãã-圌ãã¯åœŒãèªèº«ã®è£œåã«ãã¹ãŠãè©Šã¿ãŸããã ãã®ãããç«ã®äžã«ã¯ãiOSã§iFunnyãäœã§ããããããã³UIãšããŒã«ãšã³ãŒããµã³ãã«ã«é¢ããå€ãã®ãã£ãŒãããã¯ãå¿
èŠãªçç±ã«ã€ããŠã®å°ãã®æ
å ±ããããŸãã
iOSã®iFunnyãšã¯
iFunnyã¯ããŠãŒã¢ã¢ãšããŒã ã«é¢ããã¢ã¡ãªã«ã®ã¢ããªã±ãŒã·ã§ã³ã§äººæ°ããããæ¯æ1,000äžäººãèŠèŽããŠããŸãã
ããã§ãã¹ãŠãã©ã®ããã«å§ãŸã£
ããã«ã€ããŠãã£ãšèªãããšãã§ã
ãŸã ã iOSã§ã®ã¢ããªã±ãŒã·ã§ã³éçºã¯6幎åã«éå§ãããŸããããé©æ°çãªåœ±é¿ã¯ãããŸããã
- Objective-Cã®ã³ãŒãã®99ïŒ
ãä¿æããŠããŸãã
- ç§ãã¡ã¯ããã¡ããšããã¢ãžã¥ãŒã«åå²ãæã€å€å
žçãªMVCã«æºæ ããŠããŸãã
- äŸåé¢ä¿ã®ããã«Cocoapodsãç©æ¥µçã«äœ¿çšããŸãã
- ç¬èªã®webmã³ã³ãã³ããã¬ãŒã€ãŒã䜿çšããŸãããµãŒãããŒãã£ã®ãœãªã¥ãŒã·ã§ã³ã®é床ãäœäžããããã³ã³ãã³ãã®ã«ãŒããé²æ¢ããããããŸãã å®å
šã«UGCã§ããiFunnyã®å Žåããã®ãããã¯ã¯éèŠã§ãã
- SDWebImageããåå²ããŠãåçã ãã§ãªããããŠã³ããŒãããã³ã³ãã³ãã®æ®ãã«ã䜿çšããŸãã
- APIã«ã€ããŠã¯ãRestKitãéžæããŸããããã¯ãã»ãŒåé¡ã®ãªãæ°å¹Žéã®äœæ¥ã§ãããªãæçãããã¬ãŒã ã¯ãŒã¯ã§ãã
åäœãã¹ã
ç§ãã¡ã¯ãã¹ãŠãä»ã®æ¹æ³ã§åé¿ããŠããŸãïŒåããšããããšã¯ããŒã ãèŠããšããããšã§ã:)ããžãã¹ããžãã¯ãšåé¿çã®éèŠãªç¬éã«äœ¿çšããåäœãã¹ãã ããã¯éåžžã«ç°¡åãªãã¹ãã§ããã¢ãã«ã®ã¡ãœããããã¹ãããæ°ããã³ã³ãã³ãã®åä¿¡ããã§ãã¯ããŸãã
- (void)testIsNewFeaturedSetForContentArrayFalse { FNContentFeedDataSource *feedDataSource = [FNContentFeedDataSource new]; NSMutableArray *insertArray = [NSMutableArray arrayWithArray:[self baseContentArray]]; feedDataSource.currentSessionCID = @"0"; BOOL result = [feedDataSource isNewFeaturedSetForContentArray:insertArray]; XCTAssertFalse(result, @"cid check assert"); feedDataSource.currentSessionCID = @"777"; result = [feedDataSource isNewFeaturedSetForContentArray:insertArray]; XCTAssertTrue(result, @"cid check assert"); }
䜿çšãããã¹ãã®2çªç®ã®ã¯ã©ã¹ã¯ãã¯ã©ã¹ããªãŒããŒã©ã€ãããããã®ã«ãŒã«ããã§ãã¯ããããã«å¿
èŠãªãã¹ãã§ãã ããæç¹ã§ãäžé£ã®éçã¡ãœãããç°ãªããåæã·ã¹ãã çšã®åãã¿ã€ãã®ã¯ã©ã¹ãå€æ°èšè¿°ããå¿
èŠããããŸããã
XcodeãšObjective-Cã¯ã誀ã£ãŠèšè¿°ãããã³ãŒãããä¿è·ããããã®ãœãªã¥ãŒã·ã§ã³ãæäŸããŸããã§ããã
ãããã£ãŠããã®ãã¹ããäœæããŸããã
- (void)testAllAnalyticParametersClasses { NSArray *parameterClasses = [FNTestUtils classesForClassesOfType:[FNAnalyticParameter class]]; for (Class parameterClass in parameterClasses) { FNAnalyticParameter *parameter = [parameterClass value:@"TEST_VALUE"]; XCTAssertNotNil(((FNAnalyticParameter *)parameter).key); XCTAssertNotNil(((FNAnalyticParameter *)parameter).dictionary); } }
ããã§ã¯ãã¯ã©ã¹ã«2ã€ã®éçã¡ãœãããããŒãšãã£ã¯ã·ã§ããªãå«ãŸããŠããããšã確èªããŸãããããã¯ãã€ãã³ããåæã·ã¹ãã ã«æ£ããéä¿¡ããããã«å¿
èŠã§ãã
UIãã¹ã
UIèŠçŽ ã®æäœæ¹æ³ã«ã€ããŠã¯ãã§ã«ååã«ç 究ããŠãããAndroidåãã®ãã¹ããäœæããéã®ãã¹ãç°å¢ã«ã€ããŠãæ€èšããŠããŸãã 次ã®ãããªçµæã«ãªããŸããã
- æ¯åãã¹ãã§æåã§èšå®ããªãããã«ãããªã»ããã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ãèµ·åããããã®åå¥ã®ãã¬ãŒããŒã
- WireMockã䜿çšããAPIã®ã¢ãã¯ãããã«ãããæ¯åãµãŒããŒã®çãã«ãã©ãçããããã«äŸåããããšããªããªããŸãã
- ãã¹ãã®èµ·åããã»ã¹ãè©ŠããCI Bitriseã§éåžžã«äŸ¿å©ãªãããŒãèšå®ããŸãããAmazonDevice Farmã®å®éã®ããã€ã¹ã§ãã¹ããå®è¡ãããå®è¡ãããŸããBitriseã®ãªã³ã¯ãã¯ãªãã¯ãããšãã¹ã¯ãªãŒã³ã·ã§ãããšãããªãå«ãã¬ããŒãã確èªã§ããŸãã
æ°ããããŒãžã§ã³ãéçºãããã¹ãŠãèœã¡çãã®ãåŸ
ã£ãŠãããããã¹ããªãŒã ã«è¿œå ã§ããŸããã§ããã çŸåšããã¹ãããŒã¹ã®åŸ©å
ãšéçºãç©æ¥µçã«è¡ã£ãŠããŸãã
iOSã®è»¢ææãå°æ¥ããŸãããQAããŒã ãšiOSéçºè
ã¯ãåã³éãŸã£ãŠããªãèªåãã¹ããå¿
èŠãªã®ããè°è«ããããšããå§ããŸããã ããã¯éèŠãªååŒã§ããããã³ãã©ã®ããã«æ¯ãèããŸããã
- æåãã§ãã¯ãæžãããŸãã
- ååž°ãèªååããŸãã
- ã¢ããªã±ãŒã·ã§ã³ã®ç¶ç¶çãªãã¹ããæäŸããŠããã€ã§ãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ãææ¡ã§ããããã«ããŸãã
ããŒã«
ç§ãã¡ã¯ããŒã«ã®éžæããå§ããŸããã è°é¡ã«ã¯3ã€ã®äž»èŠãªãã¬ãŒã ã¯ãŒã¯ããããçŸåšã§ã¯ã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³ã®ãã¹ãã«æããã䜿çšãããŠããŸãã ãããããè©ŠããŠã¿ãŸããã
Appiumã¯äººæ°ã®ããã¯ãã¹ãã©ãããã©ãŒã ãã¬ãŒã ã¯ãŒã¯ã§ãã è¿ãå°æ¥ãã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³ããã¹ãããéã®æšæºã«ãªããšèããããŠããŸãã æ°ã¶æåãiOS 10ãå幎ãªãªãŒã¹ãããã®ã§ãã¹ãããããšã«ããŸããããå°ãåæºããŸããïŒãµããŒãä»ãã®Appiumã®ããŒãžã§ã³ã¯ããŒã¿çã§ããã補åçã§ã¯äžå®å®ãªããŒãžã§ã³ã䜿çšããããããŸããã§ããã Androidã§å®è¡ãããAppium Inspectorã䜿çšã§ããŸããã§ãããXcode8ãšiOS 10ã®ãµããŒãã¯ãããŸããã§ãããããã«å®å®ããŒãžã§ã³ããªãªãŒã¹ããŸãããã軞ãæŽæ°ããŠãã6ãæåŸ
ã€ããšã¯éåžžã«æãŸãããããŸããã ç§ãã¡ã¯èªåèªèº«ãAppiumãèŠãããªãããšã«ããŸããã
Calabashã¯ã¯ãã¹ãã©ãããã©ãŒã ã®ãªãŒãã³ãœãŒã¹ãœãªã¥ãŒã·ã§ã³ã§ããããã¹ãã®äœæã«BDDã¢ãããŒãã䜿çšããæè¿ãŸã§Xamarinã§ãµããŒããããŠããŸããã æè¿ãéçºè
ã¯ãµããŒãããã¹ãŠã ãšèšã£ãã ãŸãããã以äžé²ãŸãªãããšã«ããŸããã
ãããŠæåŸã«ã
XCTestã¯æçµçã«éžæããAppleã®ãã€ãã£ããã¬ãŒã ã¯ãŒã¯ã§ãã ãããã£ãŠãé·æã«ã€ããŠèªãã§ãã ããïŒ
- ãããžã§ã¯ãã«ããäžå¿
èŠãªäŸåé¢ä¿ã¯ãããŸããã
- Appleèªèº«ãé€ããŠãå€éšããã®èª°ããã°ãããããããè¿œå ãããããŸããã ãã§ã«AppiumãšKIFã®çµéšããããŸãã XCTestããŸã äžéšã§äœ¿çšãããŠãããAppleã®ãã°ãKIFã®ãã°ã«éããããŠããããšãããããŸãããã€ãŸãã座ã£ãŠã仲éã§ã倧ããªãã¬ãŒã ã¯ãŒã¯ãéžã¶ãšããããšã§ãã ãããã®äŸåé¢ä¿ã¯çµ¶å¯Ÿã«å¿
èŠãããŸããã§ããã
- æšæºã®iOSéçºèšèªã§ããObjective-Cããã³Swiftã䜿çšã§ããŸããQAã¯éçºè
ãšç°¡åã«ããåãã§ããŸãã
- ãã¹ã察象ã®ã¢ããªã±ãŒã·ã§ã³ã¯ãã©ãã¯ããã¯ã¹ã§ãããŸãããã¹ãã§ã¯ãã·ã¹ãã å
ã®ä»»æã®ã¢ããªã±ãŒã·ã§ã³ã§äœæ¥ã§ããŸãã
次ã«ãAppleã®ãã€ãã£ãããŒã«
Recorderãæ€èšããŸãããããã¯ãå®éã®ãã¹ããäœæãããšãã«äœ¿çšãããããšãæåŸ
ããã«ãè£å©ããŒã«ãšããŠäœçœ®ä»ããããŠããŸãã ããã䜿çšãããšãUIèŠçŽ ã®ã©ãã«ã調ã¹ãŠãåºæ¬çãªãžã§ã¹ãã£ãŒããããããšãã§ããŸãã éçºäžã«è¡ãããªãã£ãå ŽåãRecoderã¯ã³ãŒãèªäœãèšè¿°ãããªããžã§ã¯ããžã®ãã€ã³ã¿ãŒãçæããŸãã ãããå¯äžã®å©ç¹ã§ãã ããã«å€ãã®ãã€ãã¹ããããŸããïŒ
- UIã®é床ãäœäžããããããã¹ããèšé²ããã®ã¯å°é£ã§ããã¢ã¯ã·ã§ã³ãå®è¡ããŠãèšé²ããããŸã§10ã15ç§åŸ
ã¡ãŸãã äžäŸ¿ã
- ã³ãŒãã¯åžžã«ç°ãªããŸãã ä»æ¥ãç§ã¯ãšãŠãè³¢ãããã®èŠçŽ ãã¿ã³ã[1]ãææ¥ã¯ãã¹ãã€ã«ãã¿ã³ããšåŒãã§ããŸãã ãããã«ãã;
- ãžã§ã¹ãã£èªèã®æ°žç¶çãªãšã©ãŒã å·Šã«ã¹ã¯ã€ããããšãã¿ããã§ãããšå€æãããŸãã ã¿ãããããšãããã¯æ¢ã«ã¹ã¯ã€ããããŠããŸãã äžå®å®;
- Recorderã§èšé²ãããå£ãããã¹ãã¯ãå®éã®ç¶æ³ãåæ ããªããããå床å®å
šã«æžãæããå¿
èŠããããŸãã ãã ã®WTF ?!
éçºè
ã¯æ¥ãã§æ¯æŽãã
次ã«ãå®éã«çºçããåé¡ã«ã€ããŠèª¬æããŸããéçºã«é¢äžããããšã§ãããã解決ããŸãã
ãã©ãã¯ããã¯ã¹
ãã©ãã¯ããã¯ã¹ã®ãã©ã¹ã¯ãã€ãã¹ã«å€ãããŸããããã€ã¹ãŸãã¯ã·ãã¥ã¬ãŒã¿ãŒäžã®ã¢ããªã±ãŒã·ã§ã³ã®çŸåšã®ç¶æ
ãç¥ãããšã¯ã§ããŸããã ããããªã»ããããAndroidãšåæ§ã«ãã©ã®åœã§ãã©ã®ãŠãŒã¶ãŒãšããåããããããã¢ããªã±ãŒã·ã§ã³ã«éç¥ããç¹å®ã®ç°å¢ãäœæããå¿
èŠããããŸãã ããã¯ãã¹ãŠãã¢ããªã±ãŒã·ã§ã³ã®èµ·åèšå®ã䜿çšããŠè§£æ±ºãããŸãã
Xcodeã®äºåã¢ã¯ã·ã§ã³ãå¿
èŠã§ããã åãã¹ãã®åã«äœæ¥ç°å¢ããªã»ããããããã«ãã€ã³ã¹ããŒã«ãããã¢ããªã±ãŒã·ã§ã³ãã·ãã¥ã¬ãŒã¿ãŒããåé€ããŠããŠãŒã¶ãŒèšå®ãšãµã³ãããã¯ã¹ã«ä¿åãããŠãããã¹ãŠã®ãã®ããªã»ããããããšã«ããŸããã
xcrun simctl uninstall booted ${PRODUCT_BUNDLE_IDENTIFIER}
ç°å¢å€æ°ã§ã¯ã次ã®ããã«åäœããŸãã
app = [[XCUIApplication alloc] init]; app.launchEnvironment = @{ testEnviromentUserToken : @"", testEnviromentDeviceID : @"", testEnviromentCountry : @"" }; app.launchArguments = @[testArgumentNotClearStart];
ãã¹ãã§ã¯ãã¢ããªã±ãŒã·ã§ã³ãªããžã§ã¯ããäœæãããã¢ããªã±ãŒã·ã§ã³ã«è»¢éããå¿
èŠãããèšå®ãæã€ãã£ã¯ã·ã§ããªïŒãŸãã¯é
åïŒãlaunchEnviromentããã³launchArgumentsãã£ãŒã«ãã«æžã蟌ãŸããŸãã ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãèšå®ãšåŒæ°ã¯ã¡ãœããã®ã¢ããªã±ãŒã·ã§ã³ã®æåã®ããªã²ãŒãã§èªã¿èŸŒãŸããŸãã
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
ãããã£ãŠãåŠçãè¡ããŸãã
NSProcessInfo *processInfo = [NSProcessInfo processInfo]; [FNTestAPIEnviromentHandler handleArguments:processInfo.arguments enviroment:processInfo.environment];
TestAPIEnvHandlerã¯ã©ã¹ã¯ãæ§æèŸæžãšåŒæ°ã®é
åã®åŠçãå®è£
ããŸãã
ã¢ã€ãã ã®ããããã£
UIã®estã§äœæ¥ãå§ãããšããåé¡ããããŸãããæšæºã®ããŒã«ã»ããã§ã¯ãã©ã³ããšè²ã®èªã¿åããèš±å¯ãããŠããŸããã
èŠçŽ ã®ãžã§ã¹ãã£ã®ã¿ã䜿çšã§ããŸãããèŠçŽ ã«èšè¿°ãããããã¹ããèªã¿åã£ããããã®äœçœ®ãUIãã¹ãã«èå³æ·±ããã®ä»ã®ããããã£ãååŸãããããããšã¯ã§ããŸããã
代æ¿ãœãªã¥ãŒã·ã§ã³ãæ€çŽ¢ããåŸãUIãã¹ããæ©èœããAccessibility APIã«æ³šç®ããŸããã
ãã¹ããšã¢ããªã±ãŒã·ã§ã³éã®ãæ©ããšããŠãaccessibilityValueã䜿çšããããšã«ããŸãããããã¯ãiOS SDKã®ãã¹ãŠã®å¯èŠèŠçŽ ã«ãããŸãã
èªè»¢è»ãè¡ãã次ã®æ±ºå®ãå€æããŸããã
- accessibilityValueã«ãjsonæååãèšè¿°ããŸãã
- ãã¹ãã§ã¯ãèªã¿åããšãã³ãŒããè¡ããŸãã
- UIèŠçŽ ã®å Žåããã¹ãã§å¿
èŠãªãã£ãŒã«ãã®ã»ãããå®çŸ©ããã«ããŽãªãäœæããŸãã
UIButtonã®äŸã次ã«ç€ºããŸãã
@implementation UIButton (TestApi) - (NSString *)accessibilityValue { NSMutableDictionary *result = [NSMutableDictionary new]; UIColor *titleColor = [self titleColorForState:UIControlStateNormal]; CGColorRef cgColor = titleColor.CGColor; CIColor *ciColor = [CIColor colorWithCGColor:cgColor]; NSString *colorString = ciColor.stringRepresentation; if (titleColor) { [result setObject:colorString forKey:testKeyTextColor]; } return [FNTestAPIParametersParser encodeDictionary:result]; } @end
ãã¹ãã§accessibilityValueãèªã¿åãã«ã¯ãã¢ã¯ã»ã¹ããå¿
èŠããããŸã;ãã®ãããåXCUElementãªããžã§ã¯ãã«ã¯å€ãã£ãŒã«ãããããŸãã
XCUIElement *button = app.buttons[@"FeedSmile"]; NSData *stringData = [button.value dataUsingEncoding:NSUTF8StringEncoding]; NSError *error; NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:stringData options:0 error:&error];
ãŠãŒã¶ãŒã€ã³ã¿ã©ã¯ã·ã§ã³
ãžã§ã¹ãã£ãšã¢ã¯ã·ã§ã³ã®åé¡ã¯è§£æ±ºãããŸããïŒèŠãïŒïŒããŒã«èªäœã«ãã£ãŠãã¿ãããããã«ã¿ãããªã©ã®æšæºçãªæ¹æ³ã®å€§ããªã»ããã®ãããã§ãã ãããããã®ã¢ããªã±ãŒã·ã§ã³ã«ã¯ãæšæºã ãã§ãªããéåžžã«éèŠãªãã®ããããŸãã ããšãã°ãããªãã«ã¿ããã§ã¯ããã¹ãŠã®è»žã«æ²¿ã£ãŠç°ãªãæ¹åã«ã¹ã¯ã€ãããŸãã ããã解決ããããã«ããã©ã¡ãŒã¿ãŒãæ§æããã®ãšåãæšæºçãªæ¹æ³ã䜿çšããŸããã ããã¯å€§ããªç Žçã§ã¯ãããŸããã§ããã
ã¢ãããŒãã䜿çšããç°¡åãªãã¹ãã®äŸïŒ

- ç¹å®ã®èšå®ã§iFunnyãèµ·åããŸãã
- åœãéžæããŠãã ããã
- ç®çã®ãŠãŒã¶ãŒãéžæããŸãã
- è¿œå ã®èšå®ãæå®ããŸãïŒãããæåã®ã¢ããªã±ãŒã·ã§ã³èµ·åãã©ããïŒã
- ããŒãã®ãªãŒãã³ãšã³ã³ãã³ãã®ããŒãã確èªããŸãã
- ç¬é¡ãäœããŸãã
- ã³ã³ãã³ããå¹³æ»åãããŠãããïŒãã¿ã³ã®ç¶æ
ãå€æŽããããïŒUIã確èªããŸãã ã¹ã¯ããŒã«ãç¶ããŸãã
- ã¡ã«ããã¯ãèŠãŠã人çã楜ãã¿ãŸãã
- (void)testExample { XCUIElement *feedElement = app.otherElements[@"FeedContentItem"]; XCTAssertNotNil(feedElement); XCUIElement *button = app.buttons[@"FeedSmile"]; [button tap]; [[[[XCUIApplication alloc] init].otherElements[@"FeedContentItem"].scrollViews childrenMatchingType:XCUIElementTypeImage].element tap]; NSDictionary *result = [FNTestAPIParametersParser decodeString:button.value]; CIColor *color = [CIColor colorWithString:result[testKeyTextColor]]; XCTAssertFalse(color.red - 1.f < FLT_EPSILON && color.green - 0.76f < FLT_EPSILON && color.blue - 0.29f < FLT_EPSILON, @"Color not valid"); XCUIElement *feed = app.scrollViews[@"FeedContentFeed"]; [feed swipeLeft]; [feed swipeLeft]; [feed swipeLeft]; }
å®å
šãªãã¹ãã«ãã¬ããžãè¡ãäºå®ã¯ãªãã£ããããããã§å®éšãçµäºããŸããã ããã»ã¹ã§èªåãã¹ããå®å
šã«å®è£
ããããšã決å®ããå ŽåãXCtestã䜿çšããããšãæããã«ãªããŸãããããããç¶ç¶çã«å®è¡ããããšã¯éåžžã«å°é£ã§ãã çç±ã¯æ¬¡ã®ãšããã§ãã
- ããã§ãè»èŒªãåçºæããå¿
èŠããããŸãã
- QAã¯ãéçºè
ãªãã§ã¢ããªã±ãŒã·ã§ã³ãå®å
šã«ãã¹ãããããšã¯ã§ããŸããã
- 補åéçºã®ããã®UIãã¹ã-ããã¯ãã ããªãæ©èœã§ãããäŸå€çãªå Žåã«ã®ã¿é©çšã§ããŸãã
PSãã¬ãã¥ãŒãæ®åœ±ãããšããåäžã®ãã°ãå·ã€ããªãã£ãã Semyonã¯åŒãç¶ãQAããŒã ãåºæ¿ããŸãã