Development through testing (Test-driven development) is a software development technique that defines development through writing tests. In essence, you need to perform three simple repeating steps:
- Write a test for the new functionality that needs to be added;
- Write the code that will pass the test;
- To refactor new and old code.
Martin fowler
- (NSArray *)sortEvents:(NSArray *)events
- (void)testSortEvents { // given id firstEvent = [self mockEventWithClass:kCinemaEventType name:@""]; id secondEvent = [self mockEventWithClass:kCinemaEventType name:@""]; id thirdEvent = [self mockEventWithClass:kPerfomanceEventType name:@""]; id fourthEvent = [self mockEventWithClass:kPerfomanceEventType name:@""]; NSArray *correctSortedArray = @[firstEvent, secondEvent, thirdEvent, fourthEvent]; NSArray *incorrectSortedArray = @[thirdEvent, secondEvent, fourthEvent, firstEvent]; // when NSArray *sortedWithTestingMethodArray = [self.service sortEvents:incorrectSortedArray]; // then XCTAssertEqualObjects(correctSortedArray, sortedWithTestingMethodArray, @" "); }
- (NSArray *)sortEvents:(NSArray *)events { // ( , , ) NSMutableArray *cinemaEvents = [[NSMutableArray alloc] init]; NSMutableArray *otherEvents = [[NSMutableArray alloc] init]; for (Event *event in events) { if ([event.type isEqualToString:kCinemaEventType]) { [cinemaEvents addObject:event]; } else { [otherEvents addObject:event]; } } NSComparisonResult (^sortBlock)(Event *firstEvent, Event *secondEvent) = ^NSComparisonResult(Event *firstEvent, Event *secondEvent) { return [firstEvent.type compare:secondEvent.type options:NSNumericSearch]; }; [cinemaEvents sortUsingComparator:sortBlock]; [otherEvents sortUsingComparator:sortBlock]; return [cinemaEvents arrayByAddingObjectsFromArray:otherEvents]; }
- (NSArray *)mapEvents:(id)responseObject
- (void)testMapEventsResponseSuccessful { // given NSDictionary *responseObject = @{@"event" : @[@{@"type" : @1, @"name" : @"test", @"description" : @"test"}]}; // when NSArray *events = [self.mapper mapEvents:responseObject]; // then Event *event = events[0]; [self checkObject:event forKeyExistence:@[@"type", @"name", @"description"]]; }
- (void)checkObject:(id)object forKeyExistence:(NSArray *)keys { for (NSString *key in keys) { if (![object valueForKey:key]) { XCTFail(@" - %@", key); } } }
- (NSArray *)mapEvents:(id)responseObject { NSDictionary *mappingsDictionary = @{kEventResponseKey : [self eventResponseMapping]}; RKMappingResult *mappingResult = [self mapResponseObject:responseObject withMappingsDictionary:mappingsDictionary]; if (mappingResult) { return [mappingResult array]; } return @[]; }
- (void)testMapEventsResponseWithMissingMandatoryFields { // given NSDictionary *responseObject = @{kEventResponseKey : @[@{@"name" : @"test"}]}; // when NSArray *events = [self.mapper mapEvents:responseObject]; // then XCTAssertFalse([events count]); }
- (NSArray *)mapEvents:(id)responseObject { NSDictionary *mappingsDictionary = @{kEventResponseKey : [self eventResponseMapping]}; RKMappingResult *mappingResult = [self mapResponseObject:responseObject withMappingsDictionary:mappingsDictionary]; if (mappingResult) { // NSArray *events = [self checkMandatoryFields:[mappingResult array]]; return events; } return @[]; }
- (void)testMapEventsResponseWithNulls { // given NSDictionary *responseObject = @{kEventResponseKey : @[@{@"type" : @1, @"name" : [NSNull null], @"description" : [NSNull null]}]}; // when NSArray *events = [self.mapper mapEvents:responseObject]; // then Event *event = events[0]; XCTAssertNotNil(event.type, @""); XCTAssertNil(event.name, @""); }
- (void)testMapEventsResponseWithWrongType { // given NSDictionary *responseObject = @{kEventResponseKey : @[@{@"type" : @"123"}]}; // when NSArray *events = [self.mapper mapEvents:responseObject]; // then Event *event = events[0]; XCTAssertTrue([event.type isEqual:@123]); }
@interface EventService : NSObject - (instancetype)initWithClient:(id<Client>)client mapper:(id<Mapper>)mapper; - (NSArray *)obtainEventsForType:(EventType)eventType; - (void)updateEventsForType:(EventType)eventType success:(SuccessBlock)success failure:(ErrorBlock)failure; @end
@interface EventServiceTests : XCTestCase @property (nonatomic, strong) EventService *eventService; @property (nonatomic, strong) id<Client> clientMock; @property (nonatomic, strong) id<Mapper> mapperMock; @end @implementation EventServiceTests - (void)setUp { [super setUp]; [MagicalRecord setDefaultModelFromClass:[self class]]; [MagicalRecord setupCoreDataStackWithInMemoryStore]; self.clientMock = OCMProtocolMock(@protocol(Client)); self.mapperMock = OCMProtocolMock(@protocol(Mapper)); self.eventService = [[EventService alloc] initWithClient:self.clientMock mapper:self.mapperMock]; } - (void)tearDown { self.eventService = nil; self.clientMock = nil; self.mapperMock = nil; [MagicalRecord cleanUp]; [super tearDown]; }
- (void)testObtainEventsForType { // given Event *event = [Event MR_createEntity]; event.eventType = EventTypeCinema; // when NSArray *events = [self.eventService obtainEventsForType:EventTypeCinema]; // then XCTAssertEqualObjects(event, [events firstObject]); }
- (instancetype)initWithClient:(id<Client>)client mapper:(id<Mapper>)mapper { self = [super init]; if (self) { _client = client; _mapper = mapper; } return self; } - (NSArray *)obtainEventsForType:(EventType)eventType { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"eventType = %d", eventType]; NSArray *events = [Event MR_findAllWithPredicate:predicate]; return events; }
- (void)testUpdateEventsForTypeSuccessful { // given XCTestExpectation *expectation = [self expectationWithDescription:@"Callback"]; OCMStub([self.clientMock requestEventsForType:EventTypeCinema success:OCMOCK_ANY failure:OCMOCK_ANY).andDo(^(NSInvocation *invocation) { SuccessBlock block; [invocation getArgument:&block atIndex:3]; block(); }); // when [self.eventService updateEventsForType:EventTypeCinema success:^{ [expectation fulfill]; } failure:^(NSError *error) { }]; // then [self waitForExpectationsWithTimeout:DefaultTestExpectationTimeout handler:nil]; OCMVerify([self.mapperMock mapEvents:OCMOCK_ANY forType:EventTypeCinema mappingContext:OCMOCK_ANY]); }
- (void)testUpdateEventsForTypeFailure { // given XCTestExpectation *expectation = [self expectationWithDescription:@"Callback"]; NSError *clientError = [NSError errorWithDomain:@"" code:1 userInfo:nil]; OCMStub([self.clientMock requestEventsForType:EventTypeCinema success:OCMOCK_ANY failure:OCMOCK_ANY).andDo(^(NSInvocation *invocation) { ErrorBlock block; [invocation getArgument:&block atIndex:4]; block(error); }); // when [self.eventService updateEventsForType:EventTypeCinema success:^{ } failure:^(NSError *error) { XCTAssertEqual(clientError, error) [expectation fulfill]; }]; // then [self waitForExpectationsWithTimeout:DefaultTestExpectationTimeout handler:nil]; }
- (void)updateEventsForType:(EventType)eventType success:(SuccessBlock)success failure:(ErrorBlock)failure { @weakify(self); [self.client requestEventsForType:eventType success:^(id responseObject) { @strongify(self); NSManagedObjectContext *context = [NSManagedObjectContext MR_rootSavingContext]; NSArray *events = [self.mapper mapEvents:responseObject forType:eventType mappingContext:context]; [context MR_saveToPersistentStoreAndWait]; success(); } failure:^(NSError *error) { failure(error); }]; }
Development through testing encourages simple design and inspires confidence.
TDD encourages simple designs and inspires confidence.
Kent Beck
Source: https://habr.com/ru/post/263087/
All Articles