ãŠããããã¹ãã¯ãå®ç§ãªã³ãŒããæžãããã®çŽ æŽãããæ¹æ³ã§ãã ãã¹ãã¯ããããžã§ã¯ãäœæã®åæ段éã§ãšã©ãŒã®å€§éšåãèŠã€ããã®ã«åœ¹ç«ã¡ãŸãã çµéšã瀺ãããã«ãã³ãŒãã®ãã¹ããå°é£ãªå Žåããã®ãµããŒããŸãã¯ãããã°ãå°é£ã«ãªããŸãã
åäœãã¹ãã¯ãå€ç«ããããã€ã¯ãã³ã³ããŒãã³ããã§åäœããŸãã å€ãã®å Žåãã¯ã©ã¹ãããŠã§ãããã«ããå¿ èŠããããŸããã€ãŸãããã¹ãã§ããããã«ãç¹å®ã®ãã€ã¯ãã³ã³ããŒãã³ããåé¢ããããã®åœã®æ©èœå®è£ ãæäŸããŸãã Objective-Cã«ã¯ããããå®è£ ããã®ã«åœ¹ç«ã€ããã€ãã®ãµãŒãããŒãã£ãã¬ãŒã ã¯ãŒã¯ããããŸãã ãããã Swiftã§ã¯ãŸã å©çšã§ããŸããã
ãã®ã¬ã€ãã§ã¯ããåéã®èªçæ¥ãæãåºãã®ã«åœ¹ç«ã€ç°¡åãªã¢ããªã±ãŒã·ã§ã³ããã¹ãã§ã«ããŒããããã«ãç¬èªã®ã¢ãã¯ãªããžã§ã¯ããåœç©ãããã³ã¹ã¿ããäœæããæ¹æ³ãåŠç¿ããŸãã
ããå§ããŸããã
ã¹ã¿ãŒã¿ãŒãããžã§ã¯ããããŠã³ããŒãããããã¯é£çµ¡å ã¹ãã¬ãŒãžã¢ããªã±ãŒã·ã§ã³ã§ãã åºæ¬ã¢ããªã±ãŒã·ã§ã³ã®æ©èœã«ã¯åãçµã¿ãŸããã ããããã¢ããªã±ãŒã·ã§ã³ãæ£åžžã«åäœããŠããããšã確èªããããã®ãã¹ããããã€ãäœæããŸãã
ã¢ããªã±ãŒã·ã§ã³ãã³ã³ãã€ã«ããŠå®è¡ããåäœããã¹ãããŸãã ãã©ã¹ãã¿ã³ãã¯ãªãã¯ããŠã John Appleseedãå ±æé£çµ¡å ãªã¹ãã«è¿œå ããŸãã
é£çµ¡å ãä¿åããããã«ãã¢ããªã±ãŒã·ã§ã³ã¯Core Dataã䜿çšããŸãã
ãããã¯ã«ãªããªãã§ãã ããïŒ ãã®ãã¥ãŒããªã¢ã«ã§ã¯ã³ã¢ããŒã¿ã®çµéšã¯å¿ èŠãããŸããã ãã®ãããç¹å¥ãªã¹ãã«ã¯å¿ èŠãããŸããã
åäœãã¹ãã®é·æãšçæ
ãã¹ãã®äœæã«é¢ããŠã¯ãè¯ããã¥ãŒã¹ãšæªããã¥ãŒã¹ã®äž¡æ¹ã«åºããããŸãã æ®å¿µãªããšã«ããŠããããã¹ãã«ã¯æ¬¡ã®ãããªæ¬ ç¹ããããŸãã
- 倧éã®ã³ãŒãïŒãã¹ãã«ãã¬ããžã倧ãããããžã§ã¯ãã§ã¯ãæ©èœçãªã³ãŒããããå€ãã®ãã¹ããããå ŽåããããŸãã
- ããå€ãã®ãµããŒãïŒããå€ãã®ã³ãŒããããå€ãã®ãµããŒããå¿ èŠã§ãã
- é©åãªãœãªã¥ãŒã·ã§ã³ã¯ãããŸããããŠããããã¹ãã¯ä¿èšŒãããŠããããã³ãŒãã«ãšã©ãŒããªãããšãä¿èšŒããããšã¯ã§ããŸããã
- ãã£ãšæéãããããŸãïŒãã¹ãã®äœæã«ã¯æéãããããŸã-habrahabr.ruã§æ°ããæ å ±ãåŠã¶ã®ã«è²»ããããšãã§ããæéã§ãïŒ
å®å šãªè§£æ±ºçã¯ãããŸããããæããé¢ããããŸãããã¹ããæžãããšã«ã¯ã次ã®å©ç¹ããããŸãã
- èªä¿¡ïŒã³ãŒããæ©èœããããšã確èªã§ããŸãã
- ã¯ã€ãã¯ã¬ãã¥ãŒïŒãŠããããã¹ãã䜿çšãããšãããã²ãŒã·ã§ã³ã®å€ãã®ã¬ã€ã€ãŒã®äžã«é ããŠããã³ãŒããæåã§ãã¹ãã§ããŸã-æåã§ãã§ãã¯ããå¿ èŠãããã³ã³ããŒãã³ããå€ãããŸãã
- ã¢ãžã¥ãŒã«æ§ïŒãŠããããã¹ãã¯ãããã¢ãžã¥ãŒã«åãããã³ãŒãã®èšè¿°ã«éäžããã®ã«åœ¹ç«ã¡ãŸãã
- ãªãªãšã³ããŒã·ã§ã³ïŒãã€ã¯ãã³ã³ããŒãã³ãã®ãã¹ããäœæãããšã现ããéšåã«éäžã§ããŸãã
- ååž°ïŒåã«ä¿®æ£ãããšã©ãŒãä¿®æ£ããããŸãŸã§ããããã®åŸã®ä¿®æ£ãç Žæããªãããšã確èªããŠãã ããã
- ãªãã¡ã¯ã¿ãªã³ã°ïŒ Xcodeãã³ãŒããç¬èªã«æžãæããã®ã«ååã¹ããŒãã«ãªããŸã§ããªãã¡ã¯ã¿ãªã³ã°ãæ€èšŒããããã®ãŠããããã¹ããå¿ èŠã«ãªããŸãã
- ããã¥ã¡ã³ãïŒãŠããããã¹ãã§ã¯ãã³ãŒããäœããã¹ããã説æããŸãã ã³ãŒããèšè¿°ããå¥ã®æ¹æ³ã§ãã
åºæ¬çãªã¢ããªã±ãŒã·ã§ã³æ§é
ã¢ããªã±ãŒã·ã§ã³ã®å€§éã®ã³ãŒãã¯ã ã³ã¢ããŒã¿ãæå¹ã«ãªã£ãŠãããã¹ã¿ãŒ/詳现ã¢ããªã±ãŒã·ã§ã³ãã³ãã¬ãŒãã«åºã¥ããŠããŸãã ãã ããã³ãŒããã³ãã¬ãŒãã«ã¯ããã€ãã®éèŠãªæ¹åç¹ããããŸãã Xcodeã§ãããžã§ã¯ããéãããããžã§ã¯ãããã²ãŒã¿ãŒã確èªããŸãã
以äžã®è©³çŽ°ãèæ ®ããŠãã ããã
- Person.swiftãã¡ã€ã«ãšPersonInfo.swiftãã¡ã€ã«ããããŸãã Personã¯ã©ã¹ã¯NSManagedObjectã®åå«ã§ãã ãå人ã«é¢ããåºæ¬çãªæ å ±ãå«ãŸããŠããŸãã PersonInfoæ§é ã«ã¯åãæ å ±ãå«ãŸããŠããŸãããã¢ãã¬ã¹åž³ããæŽæ°ã§ããŸãã
- PeopleListãã©ã«ããŒã«ã¯ãView ControllerãããŒã¿ãããã€ããŒãããã³ããŒã¿ãããã€ããŒãããã³ã«ã®3ã€ã®ãã¡ã€ã«ããããŸãã
PeopleViewå ã®ãã¡ã€ã«ã®ã³ã¬ã¯ã·ã§ã³ã¯ã倧ããªView Controllerãåé¿ããŸãã 倧ããªView Controllerãåé¿ããããã«ãåçŽãªãããã³ã«ãä»ããŠView Controllerã«æ¥ç¶ããä»ã®ã¯ã©ã¹ã«äžéšã®è²¬ä»»ãã·ããã§ããŸãã å€ãèå³æ·±ãèšäºã§ã¯ãããŸããããã®èå³æ·±ãèšäºãèªãããšã§ã倧èŠæš¡ãªView Controllerãšãã®åé¿æ¹æ³ã«ã€ããŠåŠã¶ããšãã§ããŸãã
ãã®å Žåããããã³ã«ã¯PeopleListDataProviderProtocol.swiftã§å®çŸ©ãããŠããŸãã ãããéããŠèŠãŠãã ããã ãã®ãããã³ã«ã«æºæ ããã¯ã©ã¹ã«ã¯ãããããã£managedObjectContextããã³tableViewãå¿ èŠã§ããã addPersonïŒ_ :)ããã³fetchïŒïŒã¡ãœãããå®çŸ©ããå¿ èŠããããŸãã ããã«ã UITableViewDataSourceãããã³ã«ã«æºæ ããå¿ èŠããããŸãã
ãã¥ãŒã³ã³ãããŒã©ãŒPeopleListViewControllerã«ã¯ã PeopleListDataProviderProtocolãããã³ã«ã«æºæ ããdataProviderããããã£ããããŸãã ãã®ããããã£ã¯ãAppDelegate.swiftãã¡ã€ã«ã®PeopleListDataProviderã€ã³ã¹ã¿ã³ã¹ã«èšå®ãããŸãã
ABPeoplePickerNavigationControllerã䜿çšããŠãé£çµ¡å ãªã¹ãã«æ°ãã人ãè¿œå ããŸãã ãã®ã¯ã©ã¹ã䜿çšãããšãéçºè ã¯èš±å¯ãªãã«ãŠãŒã¶ãŒã®é£çµ¡å ã«ã¢ã¯ã»ã¹ã§ããŸãã
PeopleListDataProvider㯠ãTable Viewã«ããŒã¿ãåã蟌ã¿ã Core Dataã«ã¢ã¯ã»ã¹ãã圹å²ãæãããŸãã
泚ïŒãããžã§ã¯ãå ã®ããã€ãã®ã¯ã©ã¹ãšã¡ãœããã¯ãããªãã¯ãšããŠå®£èšãããŠããŸãã ãã¹ãã¿ãŒã²ãããã¯ã©ã¹ãšã¡ãœããã«ã¢ã¯ã»ã¹ã§ããããã«ããŸãã ãã¹ãã®å¯Ÿè±¡ã¯ãã¢ããªã±ãŒã·ã§ã³ã¢ãžã¥ãŒã«ã®å€éšã§ãã ã¢ã¯ã»ã¹ä¿®é£Ÿåãè¿œå ããªãå Žåãã¯ã©ã¹ãšã¡ãœããã¯internalãšããŠå®çŸ©ãããŸãã ããã¯ãåãã¢ãžã¥ãŒã«ã§ã®ã¿å©çšã§ããããšãæå³ããŸãã ã¢ãžã¥ãŒã«ã®å€éšããïŒããšãã°ããã¹ãã®ã¿ãŒã²ããããïŒãããã«ã¢ã¯ã»ã¹ããã«ã¯ã ãããªãã¯ã¢ã¯ã»ã¹ä¿®é£Ÿåãè¿œå ããå¿ èŠããããŸãã
ããã§ã¯ããã¹ããäœæããŸãããïŒ
ã¢ãã¯ãªããžã§ã¯ãã®äœæ
ã¢ãã¯ãªããžã§ã¯ãã䜿çšãããšãã¡ãœããåŒã³åºããè¡ãããããããããã£ãèšå®ãããŠãããã確èªã§ããŸãã ããšãã°ã PeopleListViewControllerã®viewDidLoadïŒïŒã§ã¯ãããŒãã«ãã¥ãŒãdataProviderã® tableViewããããã£ã«èšå®ãããŸã ã
å®éã«äœãèµ·ãã£ãŠããã®ãã確èªãããã¹ããäœæããŸãã
ãã¹ãçšã®ã¢ããªã±ãŒã·ã§ã³ã®æºå
ãŸãããã¹ããäœæããããã®ãããžã§ã¯ããæºåããå¿ èŠããããŸãã
ãããžã§ã¯ãããã²ãŒã¿ã§ãããžã§ã¯ããéžæããèªçæ¥ãã¹ãã¿ãŒã²ããã§ãã«ãèšå®ãéžæããŸãã 以äžã«ç€ºãããã«ã Definesã¢ãžã¥ãŒã«ãèŠã€ããŠãèšå®ãYesã«å€æŽããŸãã
次ã«ã BirthdaysTestsãã©ã«ããŒãéžæãã File \ New \ File ...ãåç §ããŸãã iOS \ Source \ Test Case ClassãéžæããŠãNextãã¯ãªãã¯ãã PeopleListViewControllerTestsãšããååãä»ãã Swiftãã¡ã€ã«ãäœæããããšã確èªããããäžåºŠNextãã¯ãªãã¯ããŠããCreateãã¯ãªãã¯ããŸã ã
Xcodeã§çµåããããŒãäœæããããã«æ±ãããããã[ ããã]ãéžæããŸãã ããã¯ãã¿ãŒã²ããã«ãã¡ã€ã«ããªããæ°ããSwiftãã¡ã€ã«ãè¿œå ãããšãã«çºçããXcodeã®ãšã©ãŒã§ãã
æ°ããäœæããPeopleListViewControllerTests.swiftãéããŸãã 以äžã«ç€ºãããã«ã import Birthdaysã¹ããŒãã¡ã³ããä»ã®importã¹ããŒãã¡ã³ãã®çŽåŸã«è¿œå ããŠããªã³ã«ããã°ããã®ã¢ãžã¥ãŒã«ãã€ã³ããŒãããŸãã
import UIKit import XCTest import Birthdays
次ã®2ã€ã®å®åçãªæ¹æ³ãåé€ããŸãã
func testExample() { // This is an example of a functional test case. XCTAssert(true, "Pass") } func testPerformanceExample() { // This is an example of a performance test case. self.measureBlock() { // Put the code you want to measure the time of here. } }
PeopleListViewControllerã®ã€ã³ã¹ã¿ã³ã¹ãå¿ èŠã«ãªã£ãããããã¹ãã§äœ¿çšã§ããŸãã
PeopleListViewControllerTestsã®äžéšã«æ¬¡ã®è¡ãè¿œå ããŸã
var viewController: PeopleListViewController!
次ã«ãsetUpïŒïŒã¡ãœããã次ã®ã³ãŒãã«çœ®ãæããŸãã
override func setUp() { super.setUp() viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("PeopleListViewController") as! PeopleListViewController }
ã¹ããŒãªãŒããŒãã䜿çšããŠPeopleListViewControllerãã€ã³ã¹ã¿ã³ã¹åãããããviewControllerã«å²ãåœãŠãŸãã
Product \ TestãéžæããŸãã Xcodeã¯ãããžã§ã¯ããã³ã³ãã€ã«ããæ¢åã®ãã¹ããå®è¡ããŸãã ãã¹ãã¯ãŸã ãããŸããããããã«ããããã¹ãŠãæ£ããæ§æãããŠããããšã確èªã§ããŸãã æ°ç§åŸã Xcodeã¯ãã¹ãŠã®ãã¹ããæåããããšãå ±åããã¯ãã§ãã
ããã§ãæåã®ã¢ãã¯ãªããžã§ã¯ããäœæããæºåãã§ããŸããã
æåã®Mockãªããžã§ã¯ããæžã
Core Dataã§äœæ¥ããããã Birthishsã®ã€ã³ããŒãè¡ã®çŽåŸã«PeopleListViewControllerTests.swiftãŸã§æ¬¡ã®ã€ã³ããŒããè¿œå ããŸãã
import CoreData
次ã«ã次ã®ã³ãŒããPeopleListViewControllerTestsã¯ã©ã¹å®çŸ©ã«è¿œå ããŸãã
class MockDataProvider: NSObject, PeopleListDataProviderProtocol { var managedObjectContext: NSManagedObjectContext? weak var tableView: UITableView! func addPerson(personInfo: PersonInfo) { } func fetch() { } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { return UITableViewCell() } }
ããã¯ããªãè€éãªã¢ãã¯ãªããžã§ã¯ãã®ããã§ãã ãã ãããã®ã¢ãã¯ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãPeopleListViewController dataProviderããããã£ã«å²ãåœãŠããããããã¯åã«æäœéå¿ èŠãªæå°éã®ãã®ã§ãã ã¢ãã¯ã¯ã©ã¹ã¯ã PeopleListDataProviderProtocolããã³UITableViewDataSourceãããã³ã«ã«ãæºæ ããå¿ èŠããããŸãã
Product \ TestãéžæããŸãã ãããžã§ã¯ããå床ã³ã³ãã€ã«ããããã¹ããUrã«åæ ŒããŸãã ããããã¢ãã¯ãªããžã§ã¯ãã䜿çšããæåã®ãŠããããã¹ãçšã«ãã¹ãŠãèšå®ã§ããŸããã
åäœãã¹ãã¯ã3ã€ã®éšåã«åããååãä»ãã æå®ããã ã ã〠ãããã³ã®é ã«ããå¿ èŠããããŸãã ' Given 'ã¯ç°å¢ãèšå®ããŸãã ' when 'ã¯ããã¹ãããå¿ èŠã®ããã³ãŒããå®è¡ããŸãã ãthenãã¯æåŸ ãããçµæã確èªããŸãã
ãã¹ãã§ã¯ã viewDidLoadïŒïŒã¡ãœããã®å®è¡åŸã«ããŒã¿ãããã€ããŒã®tableViewããããã£ãèšå®ãããŠããããšã確èªããŸãã
PeopleListViewControllerTestsã«æ¬¡ã®ãã¹ããè¿œå ããŸãã
func testDataProviderHasTableViewPropertySetAfterLoading() { // given // 1 let mockDataProvider = MockDataProvider() viewController.dataProvider = mockDataProvider // when // 2 XCTAssertNil(mockDataProvider.tableView, "Before loading the table view should be nil") // 3 let _ = viewController.view // then // 4 XCTAssertTrue(mockDataProvider.tableView != nil, "The table view should be set") XCTAssert(mockDataProvider.tableView === viewController.tableView, "The table view should be set to the table view of the data source") }
äžèšã®ãã¹ãã®ä»çµã¿ã¯æ¬¡ã®ãšããã§ãã
- MockDataProviderã®ã€ã³ã¹ã¿ã³ã¹ãäœæããdataProviderã®View Controllerããããã£ã«èšå®ããŸãã
- ãã¹ããéå§ããåã«ãtableViewããããã£ãnilã§ããããšã確èªããŸãã
- viewDidLoadïŒïŒãå®è¡ãããã¥ãŒã«ã¢ã¯ã»ã¹ã§ããŸãã
- ãã¹ãã¯ã©ã¹tableViewã®ããããã£ãnilã§ã¯ãªããããããã£ãView Controllerã®tableViewã«èšå®ãããŠããããšã確èªããŸãã
次ã«ã Product \ Testãå床éžæããŸãã ãã¹ããå®äºãããããã«ãããã²ãŒã¿ãŒãéããŸãïŒ Cmd + 5-䟿å©ãªã·ã§ãŒãã«ããããŒïŒã 次ã®ããã«è¡šç€ºãããŸãã
ã¢ãã¯ãªããžã§ã¯ãã䜿çšããæåã®ãã¹ãã¯æåããŸããïŒ
addPersonïŒ_ :)ã¡ãœããã®ãã¹ã
次ã®ãã¹ãã§ã¯ããªã¹ãããé£çµ¡å ãéžæãããšaddPersonïŒ_ :)ã¡ãœãããåŒã³åºãããããšã確èªããŸã
次ã®ããããã£ãMockDataProviderã¯ã©ã¹ã«è¿œå ããŸãã
var addPersonGotCalled = false
次ã«ã addPersonïŒ_ :)ã¡ãœããã次ã®ãã®ã«çœ®ãæããŸãã
func addPerson(personInfo: PersonInfo) { addPersonGotCalled = true }
ããã§ã addPersonïŒ_ :)ãåŒã³åºããšãã«ã trueãMockDataProviderã«èšå®ããŠã€ã³ã¹ã¿ã³ã¹ã«ç»é²ããŸãã
ãã®åäœããã¹ãããã¡ãœãããè¿œå ããåã«ã AddressBookUIãã¬ãŒã ã¯ãŒã¯ãã€ã³ããŒãããå¿ èŠããããŸãã
PeopleListViewControllerTests.swiftã«æ¬¡ã®ã€ã³ããŒããè¿œå ããŸãã
import AddressBookUI
次ã®ãã¹ãã¡ãœãããæ®ãã®ãã¹ãã¹ã¯ãªããã«è¿œå ããŸãã
func testCallsAddPersonOfThePeopleDataSourceAfterAddingAPersion() { // given let mockDataSource = MockDataProvider() // 1 viewController.dataProvider = mockDataSource // when // 2 let record: ABRecord = ABPersonCreate().takeRetainedValue() ABRecordSetValue(record, kABPersonFirstNameProperty, "TestFirstname", nil) ABRecordSetValue(record, kABPersonLastNameProperty, "TestLastname", nil) ABRecordSetValue(record, kABPersonBirthdayProperty, NSDate(), nil) // 3 viewController.peoplePickerNavigationController(ABPeoplePickerNavigationController(), didSelectPerson: record) // then // 4 XCTAssert(mockDataSource.addPersonGotCalled, "addPerson should have been called") }
ããã§äœãèµ·ãã£ãŠããã®ã§ããããïŒ
- æåã«ãView ControllerããŒã¿ãããã€ããŒãåœã®ããŒã¿ãããã€ããŒã®ã€ã³ã¹ã¿ã³ã¹ã«ã€ã³ã¹ããŒã«ããŸãã
- 次ã«ã ABPersonCreateïŒïŒã䜿çšããŠé£çµ¡å ãäœæããŸãã
- ããã§ã¯ãããªã²ãŒãã¡ãœããpeoplePickerNavigationControllerïŒ_ïŒdidSelectPerson :)ãæåã§åŒã³åºããŸãã ããªã²ãŒãã¡ãœãããæåã§æåã§åŒã³åºãããšã¯ãæªãã³ãŒãã®å åã§ããããã¹ãã®ç®çã«ã¯é©ããŠããŸãã
- æåŸã«ã addPersonïŒ_ :)ãåŒã³åºãããããšã確èªãã addPersonGotCalledãtrueã§ããããšã確èªããŸãã
Product \ TestãéžæããŠã ãã¹ããå®è¡ããŸãã ããã¯éåžžã«ç°¡åãªäœæ¥ã§ããããšãããããŸããïŒ
ããããåŸ ã£ãŠãã ããïŒ æéããããŠãã ããïŒ ãã¹ããå®éã«ããªãããã¹ããããšæããã®ããã¹ãããããšãã©ããã£ãŠç¥ã£ãŠããŸãã
ãã¹ãã確èªãã
ãã¹ããå®éã«äœãããã§ãã¯ããŠããããšã確èªããç°¡åãªæ¹æ³ã¯ããã¹ãããã§ãã¯ããŠãããªããžã§ã¯ããåé€ããããšã§ãã
PeopleListViewController.swiftãéã ã次ã®è¡peoplePickerNavigationControllerïŒ_ïŒdidSelectPerson :)ãã³ã¡ã³ãã¢ãŠãããŸã ã
dataProvider?.addPerson(person)
ãã¹ããå床å®è¡ããŸãã ä»æžããæåŸã®ãã¹ãã¯å€±æããã¯ãã§ãã åäœ-ããªãã®ãã¹ãã¯å®éã«äœãããã¹ãããŠããããšãç¥ã£ãŠããŸãã ãã¹ãã確èªããŠãã ããã å°ãªããšããæãè€éãªãã¹ãããã§ãã¯ããŠãããããæ©èœããããšã確èªããå¿ èŠããããŸãã
è¡ã®ã³ã¡ã³ãã解é€ããŠãã³ãŒããåäœç¶æ ã«æ»ããŸãã ãã¹ããå床å®è¡ããŠããã¹ãŠãæ©èœããŠããããšã確èªããŸãã
Apple Frameworkã¯ã©ã¹ã®ã¢ãã¯
NSUserDefaults.standardUserDefaultsïŒïŒãNSNotificationCenter.defaultCenterïŒïŒãªã©ã®ã·ã³ã°ã« ããŒã³ã䜿çšã§ããŸãããããã©ã«ãå€ãã©ã®ããã«ãã¹ãããŸããïŒ Appleã¯ããããã®ã¯ã©ã¹ã®ã¹ããŒã¿ã¹ã確èªããããšãèš±å¯ããŠããŸããã
æåŸ ãããçµæã®ãªãã¶ãŒããŒãšããŠãã¹ãã¯ã©ã¹ãè¿œå ã§ããŸãã ãã ãããããã®ã¯ã©ã¹ã®å®è£ ã«äŸåããããããã¹ãã®é床ãäœäžããä¿¡é Œæ§ãäœäžããå¯èœæ§ããããŸãã ãŸãã¯ãã³ãŒãã®å¥ã®éšåããå€ãèšå®ã§ããå€ç«ããåäœããã¹ãããŸããã§ããã
ãããã®å¶éãåé¿ããã«ã¯ããããã®ã·ã³ã°ã«ããŒã³ã®ä»£ããã«ã¢ãã¯ãªããžã§ã¯ãã䜿çšã§ããŸãã
æ³šïŒ Appleã¯ã©ã¹ãæš¡æ¬ãªããžã§ã¯ãã«çœ®ãæããå Žåãå®è£ ã®è©³çŽ°ã¯ãã€ã§ãå€æŽãããå¯èœæ§ãããããããã®ã¯ã©ã¹ã®åäœã§ã¯ãªãããã®ã¯ã©ã¹ãšã®çžäºäœçšã確èªããããšãéåžžã«éèŠã§ãã
ã¢ããªã±ãŒã·ã§ã³ãã³ã³ãã€ã«ããŠå®è¡ããŸãã John AppleseedãšDavid Taylorã人ã®ãªã¹ãã«è¿œå ãã ãå§ããšã åãã®éã§ãœãŒããåãæ¿ããŸãã ãªã¹ãå ã®é£çµ¡å ã®é åºã¯äžŠã¹æ¿ãã«äŸåããããšãããããŸãã
ãœãŒããæ åœããã³ãŒãã¯ã PeopleListViewController.swiftã®changeSortïŒïŒã¡ãœããã«ãããŸãã
@IBAction func changeSorting(sender: UISegmentedControl) { userDefaults.setInteger(sender.selectedSegmentIndex, forKey: "sort") dataProvider?.fetch() }
NSUserDefaultsã®ããŒã§ãœãŒãããããã«éžæãããã»ã°ã¡ã³ãã€ã³ããã¯ã¹ãè¿œå ãã fetchïŒïŒã¡ãœãããåŒã³åºããŸãã fetchïŒïŒã¡ãœããã¯ã NSUserDefaultsã§ãã®æ°ãããœãŒãé ãèªã¿åãã PeopleListDataProviderã§ç€ºãããŠããé£çµ¡å ãªã¹ããæŽæ°ããå¿ èŠããããŸãã
let sortKey = NSUserDefaults.standardUserDefaults().integerForKey("sort") == 0 ? "lastName" : "firstName" let sortDescriptor = NSSortDescriptor(key: sortKey, ascending: true) let sortDescriptors = [sortDescriptor] fetchedResultsController.fetchRequest.sortDescriptors = sortDescriptors var error: NSError? = nil if !fetchedResultsController.performFetch(&error) { println("error: \(error)") } tableView.reloadData() }
PeopleListDataProviderã¯NSFetchedResultsControllerã䜿çšããŠCore DataããããŒã¿ãååŸããŸãã ãªã¹ãã®äžŠã¹æ¿ãã眮ãæããã«ã¯ã fetchïŒïŒã¯äžŠã¹æ¿ãèšè¿°åã䜿çšããŠé åãäœæããéžæããçµæã³ã³ãããŒã©ãŒã®éžæã¯ãšãªã«èšå®ããŸãã ãã®åŸããã§ããããŠãªã¹ããæŽæ°ããããŒãã«ã§reloadDataïŒïŒã¡ãœãããåŒã³åºããŸãã
NSUserDefaultsã§ãŠãŒã¶ãŒã®åªå ãœãŒãé ãæ£ããèšå®ãããŠããããšã確èªãããã¹ããè¿œå ããŸã ã
PeopleListViewControllerTests.swiftãéã ã Mockdataproviderã¯ã©ã¹å®çŸ©ã®äžã«æ¬¡ã®ã¯ã©ã¹å®çŸ©ãè¿œå ããŸãã
class MockUserDefaults: NSUserDefaults { var sortWasChanged = false override func setInteger(value: Int, forKey defaultName: String) { if defaultName == "sort" { sortWasChanged = true } } }
MockUserDefaultsã¯NSUserDefaultsã®ãµãã¯ã©ã¹ã§ãã ããã©ã«ãå€ãfalseã® booleanããããã£sortWasChangedããããŸãã ãŸãã setIntegerïŒ_ïŒforKey :)ã¡ãœããããªãŒããŒã©ã€ããã sortWasChangedå€ãtrueã«å€æŽããŸã ã
PeopleListViewControllerTestsã¯ã©ã¹ã®æåŸã®ãã¹ãã®äžã«æ¬¡ã®ãã¹ããè¿œå ããŸãã
func testSortingCanBeChanged() { // given // 1 let mockUserDefaults = MockUserDefaults(suiteName: "testing")! viewController.userDefaults = mockUserDefaults // when // 2 let segmentedControl = UISegmentedControl() segmentedControl.selectedSegmentIndex = 0 segmentedControl.addTarget(viewController, action: "changeSorting:", forControlEvents: .ValueChanged) segmentedControl.sendActionsForControlEvents(.ValueChanged) // then // 3 XCTAssertTrue(mockUserDefaults.sortWasChanged, "Sort value in user defaults should be altered") }
ãã®ãã§ãã¯ã®ã¬ããŒãã¯æ¬¡ã®ãšããã§ãã
- æåã«MockUserDefaultsã®ã€ã³ã¹ã¿ã³ã¹ãView Controllerã®userDefaultsã«å²ãåœãŠãŸãã ãã®ææ³ã¯ãäŸåæ§æ³šå ¥ãšããŠç¥ãããŠããŸãã
- 次ã«ã UISegmentedControlã®ã€ã³ã¹ã¿ã³ã¹ãäœæããView Controllerã.ValueChangedã®ã¿ãŒã²ãããšããŠè¿œå ããŸãã
- æåŸã«ã setIntegerïŒ_ïŒforKey :)ãŠãŒã¶ãŒã®ã¢ãã¯ãããã©ã«ãã§åŒã³åºãããããšã確èªããŸãã å€ãå®éã«NSUserDefaultsã«ä¿åããããã©ããã確èªããŠããããšã«æ³šæããŠãã ããã
ãã¹ãã¹ã€ãŒããå®è¡ããŸãããã¹ãŠãæ£åžžã«å®äºããã¯ãã§ãã
æ¬åœã«è€éãªAPIãŸãã¯ãã¬ãŒã ã¯ãŒã¯ãæã£ãŠããããå°ããªã³ã³ããŒãã³ããæ¬åœã«ãã¹ããããå Žåã¯ããã¬ãŒã ã¯ãŒã¯ãæ·±ãæãäžããªãã§ãã ããã
ããã¯ãããªããããããåœé ãããäœæããªããšãã§ãïŒ ïŒ]
åœç©ãªããžã§ã¯ãã®äœæ
åœç©ãªããžã§ã¯ãã¯ãåœç©ã®ã¯ã©ã¹ã®å®å šãªå®è£ ã®ããã«åäœããŸãã ãããã¯ãæ±ãã«ããã¯ã©ã¹ãŸãã¯æ§é ã®ä»£ãããšããŠäœ¿çšããŸãã
ã¢ããªã±ãŒã·ã§ã³ã®å Žåãã¬ã³ãŒããè¿œå ããŠCore Dataã§éžæããå¿ èŠã¯ãããŸããã ãã®ããã代ããã«Core Dataãåœé ããŸãã å°ãæãããã§ããã
BirthdaysTestsãã©ã«ããŒãéžæãã File \ New \ File ...ã«ç§»åããŸãã iOS \ Source \ Test Case Classãã³ãã¬ãŒããéžæãã Nextãã¯ãªãã¯ããŸãã ã¯ã©ã¹ã«PeopleListDataProviderTestsãšããååãä»ããã 次ãžããã¯ãªãã¯ããŠããã äœæ ããã¯ãªãã¯ããŸã ã
äœæãããã¹ãã¯ã©ã¹ã®äžèŠãªãã¹ããå床åé€ããŸãã
func testExample() { // ... } func testPerformanceExample() { // ... }
次ã®2ã€ã®ã€ã³ããŒããæ°ããã¯ã©ã¹ã«è¿œå ããŸãã
import Birthdays import CoreData
次ã«ã次ã®ããããã£ãè¿œå ããŸãã
var storeCoordinator: NSPersistentStoreCoordinator! var managedObjectContext: NSManagedObjectContext! var managedObjectModel: NSManagedObjectModel! var store: NSPersistentStore! var dataProvider: PeopleListDataProvider!
ããããã£ã«ã¯ã ã³ã¢ããŒã¿ã¹ã¿ãã¯ã§äœ¿çšãããäž»èŠã³ã³ããŒãã³ããå«ãŸããŠããŸãã Core Dataã®äœ¿çšãéå§ããã«ã¯ã Core Dataãã¥ãŒããªã¢ã«ãã芧ãã ããã
次ã®ã³ãŒããsetUpïŒïŒã¡ãœããã«è¿œå ããŸãã
// 1 managedObjectModel = NSManagedObjectModel.mergedModelFromBundles(nil) storeCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) store = storeCoordinator.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, URL: nil, options: nil, error: nil) managedObjectContext = NSManagedObjectContext() managedObjectContext.persistentStoreCoordinator = storeCoordinator // 2 dataProvider = PeopleListDataProvider() dataProvider.managedObjectContext = managedObjectContext
äžèšã®ã³ãŒãã§èµ·ããããšã¯æ¬¡ã®ãšããã§ãã
- setUpïŒïŒã¯ãã¡ã¢ãªå ã®ã¹ãã¬ãŒãžã䜿çšããŠç®¡ç察象ãªããžã§ã¯ãã®ã³ã³ããã¹ããäœæããŸãã éåžžã ã³ã¢ããŒã¿ã¯ããã€ã¹ã®ãã¡ã€ã«ã·ã¹ãã å ã®ãã¡ã€ã«ã§ãã ãããã®ãã¹ãã§ã¯ãããã€ã¹ã®ã¡ã¢ãªã«ãæ°žç¶çãªãã¹ãã¬ãŒãžãäœæããŸãã
- 次ã«ãPeopleListDataProviderã®ã€ã³ã¹ã¿ã³ã¹ãšã管ç察象ãªããžã§ã¯ãã³ã³ããã¹ããäœæãã管ç察象ãªããžã§ã¯ãã³ã³ããã¹ãã管ç察象ãªããžã§ã¯ãã³ã³ããã¹ããšããŠèšå®ãããã¡ã¢ãªã«ä¿åããŸãã ããã¯ãæ°ããããŒã¿ãããã€ããŒãå®éã®ããã«åäœããããšãæå³ããŸãããCore Dataã§ãªããžã§ã¯ããè¿œå ãŸãã¯åé€ããŸããã
次ã®2ã€ã®ããããã£ãPeopleListDataProviderTestsã«è¿œå ããŸãã
var tableView: UITableView! var testRecord: PersonInfo!
次ã«ã setUpïŒïŒã¡ãœããã®æåŸã«æ¬¡ã®ã³ãŒããè¿œå ããŸãã
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("PeopleListViewController") as! PeopleListViewController viewController.dataProvider = dataProvider tableView = viewController.tableView testRecord = PersonInfo(firstName: "TestFirstName", lastName: "TestLastName", birthday: NSDate())
ããã«ãããã¹ããŒãªãŒããŒãã§View Controllerãã€ã³ã¹ã¿ã³ã¹åããŠTable Viewãã»ããã¢ãããããã¹ãã§äœ¿çšããPersonInfoã®ã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã
ãã¹ããå®äºãããã管ç察象ãšã³ãã£ãã£ã®ã³ã³ããã¹ããããªã»ãããããå¿ èŠããããŸãã
tearDownïŒïŒã¡ãœããã次ã®ã³ãŒãã«çœ®ãæããŸãã
override func tearDown() { managedObjectContext = nil var error: NSError? = nil XCTAssert(storeCoordinator.removePersistentStore(store, error: &error), "couldn't remove persistent store: \(error)") super.tearDown() }
ãã®ã³ãŒãã¯ãmanagedObjectContextãnilã«èšå®ããŠã¡ã¢ãªã解æŸããã¹ãã¢ã³ãŒãã£ããŒã¿ãŒããæ°žç¶ã¹ãã¢ãåé€ããŸãã æ°ãããã¹ããªããžããªã䜿çšããŠåãã¹ããå®è¡ã§ããŸãã
ä»-ãã¹ããæžãããšãã§ããŸãïŒ æ¬¡ã®ãã¹ãããã¹ãã¯ã©ã¹ã«è¿œå ããŸãã
func testThatStoreIsSetUp() { XCTAssertNotNil(store, "no persistent store") }
ãã®ãã¹ãã§ã¯ãã¹ãã¬ãŒãžã空ã§ã¯ãªãããšã確èªããŸãã æ°ãããã¹ããå®è¡ããŸã-ãã¹ãŠãæåããã¯ãã§ãã
次ã®ãã¹ãã§ã¯ãããŒã¿ãœãŒã¹ãäºæ³ãããè¡æ°ãæäŸãããã©ããã確èªããŸãã
次ã®ãã¹ãããã¹ãã¯ã©ã¹ã«è¿œå ããŸãã
func testOnePersonInThePersistantStoreResultsInOneRow() { dataProvider.addPerson(testRecord) XCTAssertEqual(tableView.dataSource!.tableView(tableView, numberOfRowsInSection: 0), 1, "After adding one person number of rows is not 1") }
æåã«é£çµ¡å ããã¹ããªããžããªã«è¿œå ããŠãããè¡æ°ã1ã§ããããšã確èªããŸãã
ãã¹ããå®è¡ããŸã-ãã¹ãŠæåããã¯ãã§ãã
åœã®ãæ°žç¶çãªãã¹ãã¬ãŒãžãäœæããããšã«ãããè¿ éãªãã¹ããæäŸãããã£ã¹ã¯ãã¯ãªãŒã³ãªç¶æ ã«ä¿ã€ããšãã§ãããããèµ·åæã«ã¢ããªã±ãŒã·ã§ã³ãæåŸ ã©ããã«åäœããããšã確èªã§ããŸãã
ãã¹ããæžãã2ã€ä»¥äžã®ãã¹ãé£çµ¡å ãè¿œå ããåŸãã»ã¯ã·ã§ã³ãšè¡ã®æ°ã確èªããããšãã§ããŸãã ããã¯ãã¹ãŠããããžã§ã¯ãã§éæããããšããŠããèªä¿¡ã®ã¬ãã«ã«äŸåããŸãã
ãããžã§ã¯ãã§äžåºŠã«è€æ°ã®ããŒã ãšäœæ¥ããããšãããå Žåããããžã§ã¯ãã®ãã¹ãŠã®éšåãåæã«æºåãã§ããŠããããã§ã¯ãªãããšãç¥ã£ãŠããŸããããã§ã«ã³ãŒãããã¹ãããå¿ èŠããããŸãã ããããWebãµãŒãã¹ãªã©ãååšããªããã®ã«ã€ããŠã³ãŒãã®äžéšãã©ã®ããã«ãã¹ãã§ããŸããïŒ
ã¹ã¿ããããªãã®å©ãã«ãªããŸãïŒ
ã¹ã¿ããæžã
ã¹ã¿ãã¯ãªããžã§ã¯ãã¡ãœããåŒã³åºããžã®å¿çãåœè£ ããŸãã ã¹ã¿ãã䜿çšããŠããŸã éçºäžã®WebãµãŒãã¹åŒã³åºãã³ãŒãããã¹ãããŸãã
ãããžã§ã¯ãã®WebããŒã ã¯ãã¢ããªã±ãŒã·ã§ã³ãšåãæ©èœãåããWebãµãŒãã¹ã®äœæãä»»ãããŸããã ãŠãŒã¶ãŒã¯ãµãŒãã¹ã«ã¢ã«ãŠã³ããäœæããã¢ããªã±ãŒã·ã§ã³ãšãµãŒãã¹éã§ããŒã¿ãåæã§ããŸãã ããããWebããŒã ã¯äœæ¥ã®äžéšãéå§ããããšããããªãã£ããããã»ãŒå®äºã§ãã ã¹ã¿ããäœæããŠWebãµãŒããŒã³ã³ããŒãã³ãã眮ãæããå¿ èŠãããããã§ãã
ãã®ã»ã¯ã·ã§ã³ã§ã¯ã2ã€ã®ã¡ãœããã®ãã¹ãã®äœæã«çŠç¹ãåœãŠãŸãã1ã€ã¯ãµã€ãã«è¿œå ãããé£çµ¡å ãéžæããæ¹æ³ããã1ã€ã¯ã¢ããªã±ãŒã·ã§ã³ããWebãµãŒãã¹ã«é£çµ¡å ãè¿œå ããæ¹æ³ã§ãã å®éã®ã·ããªãªã§ã¯ããŠãŒã¶ãŒåãšã¢ã«ãŠã³ããããã³ãšã©ãŒåŠçãå¿ èŠã«ãªããŸãããå¥ã®ãšãã«è¡ããŸãã
APICommunicatorProtocol.swiftãéããŸã ã ãã®ãããã³ã«ã¯ãWebãµãŒãã¹ããé£çµ¡å ãåä¿¡ããé£çµ¡å ãè¿œå ããããã®2ã€ã®ã¡ãœããã宣èšããŸãã
Personã®ã€ã³ã¹ã¿ã³ã¹ã移åã§ããŸããã管ç察象ãšã³ãã£ãã£ã«ã¯å¥ã®ã³ã³ããã¹ããå¿ èŠã§ãã ãã®å Žåãæ§é ã®äœ¿çšãã¯ããã«ç°¡åã«ãªããŸããã
次ã«ãã¹ã¿ããäœæããŠãView ControllerãšAPICommunicatorã€ã³ã¹ã¿ã³ã¹ã®çžäºäœçšããµããŒãããŸãã
PeopleListViewControllerTests.swiftãéãã PeopleListViewControllerTestsã¯ã©ã¹å ã«æ¬¡ã®ã¯ã©ã¹å®çŸ©ãè¿œå ããŸãã
// 1 class MockAPICommunicator: APICommunicatorProtocol { var allPersonInfo = [PersonInfo]() var postPersonGotCalled = false // 2 func getPeople() -> (NSError?, [PersonInfo]?) { return (nil, allPersonInfo) } // 3 func postPerson(personInfo: PersonInfo) -> NSError? { postPersonGotCalled = true return nil } }
泚æãã¹ãããšïŒ
- APICommunicatorãæ§é äœã§ãã£ãŠããã¢ãã¯ã®å®è£ ã¯ã¯ã©ã¹ã§ãã ãã®å Žåããã¹ãã§ã¯ããŒã¿ãå€æŽããå¿ èŠããããããã¯ã©ã¹ã䜿çšããæ¹ã䟿å©ã§ãã ããã¯ãæ§é ãããã¯ã©ã¹ã§è¡ãæ¹ãå°ãç°¡åã§ãã
- getPeopleïŒïŒã¡ãœããã¯ãallPersonInfoã«ä¿åãããŠãããã®ãè¿ããŸãã ãããã¯ãŒã¯ããããŒã¿ãããŠã³ããŒããã代ããã«ãåçŽãªé åã«é£çµ¡å æ å ±ãä¿åããã ãã§ãã
- postPersonïŒ_ :)ã¡ãœããã¯ãpostPersonGotCalledã«trueãèšå®ããŸãã
ã¹ã¿ãAPIããã¹ãããŠã addPersonïŒïŒã¡ãœãããåŒã³åºãããšãã«ãAPIããè¿ããããã¹ãŠã®é£çµ¡å ããªããžããªã«è¿œå ãããããšã確èªããŸã
PeopleListViewControllerTestsã«æ¬¡ã®ãã¹ãã¡ãœãããè¿œå ããŸãã
func testFetchingPeopleFromAPICallsAddPeople() { // given // 1 let mockDataProvider = MockDataProvider() viewController.dataProvider = mockDataProvider // 2 let mockCommunicator = MockAPICommunicator() mockCommunicator.allPersonInfo = [PersonInfo(firstName: "firstname", lastName: "lastname", birthday: NSDate())] viewController.communicator = mockCommunicator // when viewController.fetchPeopleFromAPI() // then // 3 XCTAssert(mockDataProvider.addPersonGotCalled, "addPerson should have been called") }
äžèšã®ã³ãŒãã§èµ·ããããšã¯æ¬¡ã®ãšããã§ãã
- æåã«ããã¹ãã§äœ¿çšããmockDataProviderããã³mockCommunicatorã·ãã¥ã¬ãŒããªããžã§ã¯ããæ§æããŸãã
- 次ã«ãåœã®é£çµ¡å ãäœæãã fetchPeopleFromAPIïŒïŒã¡ãœãããåŒã³åºããŠåœã®ãããã¯ãŒã¯åŒã³åºããè¡ããŸãã
- æåŸã«ã addPersonïŒ_ :)ã¡ãœããããã¹ãããŸãã
ãã¹ããã³ã³ãã€ã«ããŠå®è¡ããŸãã
ããã§ã¯ã次ã¯äœã§ããïŒ
ãããžã§ã¯ãã®æçµããŒãžã§ã³ãããŠã³ããŒãããŸã ããã®ããŒãžã§ã³ã«ã¯ããã®èšäºã§ã¯èª¬æããªãã£ãè¿œå ã®ãã¹ããå«ãŸããŠããŸãã
ã¢ããªã±ãŒã·ã§ã³ã®ãã€ã¯ãã³ã³ããŒãã³ãããã¹ãããããã®æš¡æ¬ãªããžã§ã¯ããåœç©ãããã³ã¹ã¿ãã®äœææ¹æ³ãåŠã³ã Swift㧠XCTestãã©ã®ããã«æ©èœããããç解ããŸããã
ãã®èšäºã§ã¯ããã¹ãã®æåã®ç解ã®ã¿ã瀺ããŸãã ã¢ããªã±ãŒã·ã§ã³ã®ãã¹ããäœæããããã®ã¢ã€ãã¢ããã§ã«ãããšç¢ºä¿¡ããŠããŸãã
åäœãã¹ãã®è©³çŽ°ã«ã€ããŠã¯ã ãã¹ãé§åéçºïŒTDDïŒããã³åäœé§åéçºïŒBDDïŒãã芧ãã ããã ãããã¯ãã³ãŒããæžãåã«ãã¹ããæžãã¢ããªã±ãŒã·ã§ã³éçºæ¹æ³è«ã§ãïŒãããŠãççŽã«èšã£ãŠããŸã£ããæ°ããèãæ¹ãè¡šããŸãïŒã
åäœãã¹ãã¯ãå®å šãªãã¹ãã¹ã€ãŒãã®äžéšã«ãããŸããã å æ¬çãªãã¹ãã¯æ¬¡ã®è«ççãªã¹ãããã§ãã å æ¬çãªãã¹ããéå§ããç°¡åãªæ¹æ³ã¯ã UIAutomationã䜿çšããããšã§ãã ã¢ããªã±ãŒã·ã§ã³ã®ãã¹ããçå£ã«èããŠããå Žåã¯ã UIAutomationã䜿çšããå¿ èŠããããŸãã
psãã®èšäºã¯2015幎9æ9æ¥ããåã«äœæãããããããµã³ãã«ã®äœæã«ã¯SwiftããŒãžã§ã³1.2ã䜿çšãããŸããã Swiftèšèªã®æ°ããããŒãžã§ã³ã®ãªãªãŒã¹ã«é¢é£ããŠãäŸã«ããã€ãã®å€æŽãå ããŸããã ãããžã§ã¯ãã®ãœãŒã¹ã³ãŒãã¯ã ãã¡ããšãã¡ãã«ãããŸã ã