Skip to content

Commit

Permalink
Support XCTest assertions by forwarding messages to the current example
Browse files Browse the repository at this point in the history
Allows the use of XCTest assertions in specs. This should include other
matcher libraries implemented in terms of XCTest assertions.

- Within `+buildExampleGroups`, defines a shadow `self` pointer that is
  of the type of the test case instance.
- Unrecognized selectors sent to the test case class are forwarded to
  the current example, and then to the current test case instance.
- Adds a test for the use of XCTest assertions within specs.
  • Loading branch information
sharplet committed Aug 19, 2015
1 parent 053c8e6 commit 8f2727c
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 4 deletions.
4 changes: 3 additions & 1 deletion Classes/Core/KWExample.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#import "KWReporting.h"
#import "KWExampleDelegate.h"

#import <XCTest/XCTestCase.h>

@class KWCallSite;
@class KWExampleSuite;
@class KWContextNode;
Expand All @@ -40,7 +42,7 @@

#pragma mark - Running

- (void)runWithDelegate:(id<KWExampleDelegate>)delegate;
- (void)runWithDelegate:(XCTestCase<KWExampleDelegate> *)delegate;

#pragma mark - Anonymous It Node Descriptions

Expand Down
18 changes: 16 additions & 2 deletions Classes/Core/KWExample.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ @interface KWExample ()

@property (nonatomic, readonly) NSMutableArray *verifiers;
@property (nonatomic, readonly) KWMatcherFactory *matcherFactory;
@property (nonatomic, weak) id<KWExampleDelegate> delegate;
@property (nonatomic, weak) XCTestCase<KWExampleDelegate> *delegate;
@property (nonatomic, assign) BOOL didNotFinish;
@property (nonatomic, strong) id<KWExampleNode> exampleNode;
@property (nonatomic, assign) BOOL passed;
Expand Down Expand Up @@ -71,6 +71,20 @@ - (NSString *)description {
return [NSString stringWithFormat:@"<KWExample: %@>", 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<KWVerifying>)aVerifier {
Expand Down Expand Up @@ -105,7 +119,7 @@ - (id)addAsyncVerifierWithExpectationType:(KWExpectationType)anExpectationType c

#pragma mark - Running examples

- (void)runWithDelegate:(id<KWExampleDelegate>)delegate; {
- (void)runWithDelegate:(XCTestCase<KWExampleDelegate> *)delegate {
self.delegate = delegate;
[self.matcherFactory registerMatcherClassesWithNamespacePrefix:@"KW"];
[[KWExampleSuiteBuilder sharedExampleSuiteBuilder] setCurrentExample:self];
Expand Down
20 changes: 20 additions & 0 deletions Classes/Core/KWSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions Classes/Core/KiwiMacros.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Tests/KWObjCXCTestAssertionTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
SPEC_BEGIN(KWObjCXCTestAssertionTests)

describe(@"XCTest assertions in Objective C", ^{
pending(@"supports XCTAssert", ^{
it(@"supports XCTAssert", ^{
XCTAssert(1 + 1 == 2);
});
});
Expand Down

0 comments on commit 8f2727c

Please # to comment.