diff --git a/Classes/Core/KWExample.h b/Classes/Core/KWExample.h index 3a6fa553..58777f61 100644 --- a/Classes/Core/KWExample.h +++ b/Classes/Core/KWExample.h @@ -14,6 +14,8 @@ #import "KWReporting.h" #import "KWExampleDelegate.h" +#import + @class KWCallSite; @class KWExampleSuite; @class KWContextNode; @@ -40,7 +42,7 @@ #pragma mark - Running -- (void)runWithDelegate:(id)delegate; +- (void)runWithDelegate:(XCTestCase *)delegate; #pragma mark - Anonymous It Node Descriptions diff --git a/Classes/Core/KWExample.m b/Classes/Core/KWExample.m index e7885154..6aab7aa2 100644 --- a/Classes/Core/KWExample.m +++ b/Classes/Core/KWExample.m @@ -32,7 +32,7 @@ @interface KWExample () @property (nonatomic, readonly) NSMutableArray *verifiers; @property (nonatomic, readonly) KWMatcherFactory *matcherFactory; -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) XCTestCase *delegate; @property (nonatomic, assign) BOOL didNotFinish; @property (nonatomic, strong) id exampleNode; @property (nonatomic, assign) BOOL passed; @@ -71,6 +71,20 @@ - (NSString *)description { return [NSString stringWithFormat:@"", self.exampleNode.description]; } +#pragma mark - Message forwarding + +- (id)forwardingTargetForSelector:(SEL)aSelector { + if ([self.delegate respondsToSelector:aSelector]) { + return self.delegate; + } else { + return [super forwardingTargetForSelector:aSelector]; + } +} + +- (BOOL)respondsToSelector:(SEL)aSelector { + return [super respondsToSelector:aSelector] || [self.delegate respondsToSelector:aSelector]; +} + #pragma mark - Adding Verifiers - (id)addVerifier:(id)aVerifier { @@ -105,7 +119,7 @@ - (id)addAsyncVerifierWithExpectationType:(KWExpectationType)anExpectationType c #pragma mark - Running examples -- (void)runWithDelegate:(id)delegate; { +- (void)runWithDelegate:(XCTestCase *)delegate { self.delegate = delegate; [self.matcherFactory registerMatcherClassesWithNamespacePrefix:@"KW"]; [[KWExampleSuiteBuilder sharedExampleSuiteBuilder] setCurrentExample:self]; diff --git a/Classes/Core/KWSpec.m b/Classes/Core/KWSpec.m index d9926b18..c6526a19 100644 --- a/Classes/Core/KWSpec.m +++ b/Classes/Core/KWSpec.m @@ -81,6 +81,26 @@ + (NSInvocation *)invocationForExample:(KWExample *)example selector:(SEL)select return invocation; } +#pragma mark - Message forwarding + +// Inside `SPEC_BEGIN`/`SPEC_END`, `self` references the spec class object but +// ultimately needs to instead behave like an _instance_ of `KWSpec`. The current +// example has a reference to the running test case via its delegate, and so +// forwarding messages there enables messages sent to `self` from within an +// example to behave just like the test case instance. +// +// This is useful, for example, because the `XCTAssert*` macros require a `self` +// that is a kind of `XCTestCase`. ++ (id)forwardingTargetForSelector:(SEL)aSelector { + KWExample *example = [[KWExampleSuiteBuilder sharedExampleSuiteBuilder] currentExample]; + + if ([example respondsToSelector:aSelector]) { + return example; + } else { + return [super forwardingTargetForSelector:aSelector]; + } +} + #pragma mark - Running Specs - (void)runExample { diff --git a/Classes/Core/KiwiMacros.h b/Classes/Core/KiwiMacros.h index af932ed1..13fcfa4e 100644 --- a/Classes/Core/KiwiMacros.h +++ b/Classes/Core/KiwiMacros.h @@ -89,8 +89,14 @@ + (NSString *)file { return @__FILE__; } \ \ + (void)buildExampleGroups { \ + id _kw_test_case_class = self; \ + { \ + /* The shadow `self` must be declared inside a new scope to avoid compiler warnings. */ \ + /* The receiving class object delegates unrecognized selectors to the current example. */ \ + __unused name *self = _kw_test_case_class; #define SPEC_END \ + } \ } \ \ @end diff --git a/Tests/KWObjCXCTestAssertionTests.m b/Tests/KWObjCXCTestAssertionTests.m index fb9506b8..80583d4e 100644 --- a/Tests/KWObjCXCTestAssertionTests.m +++ b/Tests/KWObjCXCTestAssertionTests.m @@ -3,7 +3,7 @@ SPEC_BEGIN(KWObjCXCTestAssertionTests) describe(@"XCTest assertions in Objective C", ^{ - pending(@"supports XCTAssert", ^{ + it(@"supports XCTAssert", ^{ XCTAssert(1 + 1 == 2); }); });