From 7fa24afdc8b521e2a009e2d32b28172685ac616e Mon Sep 17 00:00:00 2001 From: Dave MacLachlan Date: Thu, 7 May 2020 08:43:23 -0700 Subject: [PATCH] Error when ignoreNonObjectArgs is unnecessary. This encourages proper use of the APIS and keeps the testing code cleaner and easier to read. --- Source/OCMock/OCMInvocationMatcher.m | 25 ++++++++++++++++++ Source/OCMockTests/OCMockObjectMacroTests.m | 3 +-- Source/OCMockTests/OCMockObjectTests.m | 28 +++++++++++++++++++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Source/OCMock/OCMInvocationMatcher.m b/Source/OCMock/OCMInvocationMatcher.m index 55162843..df68c9c3 100644 --- a/Source/OCMock/OCMInvocationMatcher.m +++ b/Source/OCMock/OCMInvocationMatcher.m @@ -45,6 +45,7 @@ - (void)setInvocation:(NSInvocation *)anInvocation // anInvocation contains self as an argument, -retainArguments would create a retain cycle. [anInvocation retainObjectArgumentsExcludingObject:self]; recordedInvocation = [anInvocation retain]; + [self verifyInvocationCompatibleWithIgnoreNonObjectArgs]; } - (void)setRecordedAsClassMethod:(BOOL)flag @@ -60,6 +61,7 @@ - (BOOL)recordedAsClassMethod - (void)setIgnoreNonObjectArgs:(BOOL)flag { ignoreNonObjectArgs = flag; + [self verifyInvocationCompatibleWithIgnoreNonObjectArgs]; } - (NSString *)description @@ -139,4 +141,27 @@ - (BOOL)matchesInvocation:(NSInvocation *)anInvocation return YES; } +- (void)verifyInvocationCompatibleWithIgnoreNonObjectArgs +{ + if (!recordedInvocation || !ignoreNonObjectArgs) + { + return; + } + NSMethodSignature *signature = [recordedInvocation methodSignature]; + NSUInteger n = [signature numberOfArguments]; + BOOL foundNonObjectArg = NO; + for(NSUInteger i = 2; i < n; i++) + { + if(!OCMIsObjectType([signature getArgumentTypeAtIndex:i])) + { + foundNonObjectArg = YES; + break; + } + } + if (!foundNonObjectArg) + { + [NSException raise:NSInvalidArgumentException format:@"Method `%@` with %@ marked as ignoreNonObjectArgs.", NSStringFromSelector([recordedInvocation selector]), n == 2 ? @"0 args" : @"only object args"]; + } +} + @end diff --git a/Source/OCMockTests/OCMockObjectMacroTests.m b/Source/OCMockTests/OCMockObjectMacroTests.m index 3c77854c..3d89d069 100644 --- a/Source/OCMockTests/OCMockObjectMacroTests.m +++ b/Source/OCMockTests/OCMockObjectMacroTests.m @@ -552,7 +552,6 @@ - (void)testReturnsCorrectObjectFromInitMethodCalledOnRecorderInsideMacro // used and we're now making sure that a return value is specified for init methods. id mock = OCMClassMock([NSString class]); OCMStub([[mock andReturn:nil] initWithString:OCMOCK_ANY]); - OCMStub([[mock ignoringNonObjectArgs] initWithString:OCMOCK_ANY]); OCMStub([[mock andReturnValue:nil] initWithString:OCMOCK_ANY]); OCMStub([[mock andThrow:nil] initWithString:OCMOCK_ANY]); OCMStub([[mock andPost:nil] initWithString:OCMOCK_ANY]); @@ -565,7 +564,7 @@ - (void)testReturnsCorrectObjectFromInitMethodCalledOnRecorderInsideMacro _OCMVerify([(id)[mock withQuantifier:nil] initWithString:OCMOCK_ANY]); // Test multiple levels of recorder methods. - OCMStub([[[[mock ignoringNonObjectArgs] andReturn:nil] andThrow:nil] initWithString:OCMOCK_ANY]); + OCMStub([[[mock andReturn:nil] andThrow:nil] initWithString:OCMOCK_ANY]); } - (void)testStubMacroPassesExceptionThrough diff --git a/Source/OCMockTests/OCMockObjectTests.m b/Source/OCMockTests/OCMockObjectTests.m index 27543673..bde9deb6 100644 --- a/Source/OCMockTests/OCMockObjectTests.m +++ b/Source/OCMockTests/OCMockObjectTests.m @@ -489,9 +489,10 @@ - (void)testRaisesExceptionWhenMethodWithMixedArgumentsIsCalledWithWrongObjectAr - (void)testBlocksAreNotConsideredNonObjectArguments { - [[[mock stub] ignoringNonObjectArgs] enumerateLinesUsingBlock:[OCMArg invokeBlock]]; + mock = [OCMockObject mockForClass:[NSArray class]]; + [[[mock stub] ignoringNonObjectArgs] enumerateObjectsWithOptions:0 usingBlock:[OCMArg invokeBlock]]; __block BOOL blockWasInvoked = NO; - [mock enumerateLinesUsingBlock:^(NSString *_Nonnull line, BOOL *_Nonnull stop) { + [mock enumerateObjectsWithOptions:5 usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { blockWasInvoked = YES; }]; XCTAssertTrue(blockWasInvoked, @"Should not have ignored the block argument."); @@ -503,6 +504,29 @@ - (void)testThrowsWhenAttemptingToStubMethodOnStoppedMock XCTAssertThrowsSpecificNamed([[mock stub] rangeOfString:@"foo" options:0], NSException, NSInternalInconsistencyException); } +- (void)testRaisesExceptionWhenIgnoringNonObjectArgumentsOnMethodThatHasZeroArgs +{ + @try + { + [[[mock stub] ignoringNonObjectArgs] uppercaseString]; + } + @catch (NSException *e) + { + XCTAssertTrue([[e reason] containsString:@"0 args"]); + } +} + +- (void)testRaisesExceptionWhenIgnoringNonObjectArgumentsOnMethodThatOnlyTakesObjects +{ + @try + { + [[[mock stub] ignoringNonObjectArgs] stringByAppendingString:[OCMArg any]]; + } + @catch (NSException *e) + { + XCTAssertTrue([[e reason] containsString:@"only object args"]); + } +} #pragma mark returning values from stubbed methods