8000 Introduce `Behavior<Context>` by mosamer · Pull Request #701 · Quick/Quick · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Introduce Behavior<Context> #701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Quick.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@
AED9C8641CC8A7BD00432F62 /* CrossReferencingSpecs.swift in Sources */ = {isa = PBXBuildFile; fileRef = AED9C8621CC8A7BD00432F62 /* CrossReferencingSpecs.swift */; };
AED9C8651CC8A7BD00432F62 /* CrossReferencingSpecs.swift in Sources */ = {isa = PBXBuildFile; fileRef = AED9C8621CC8A7BD00432F62 /* CrossReferencingSpecs.swift */; };
CD264DBD1DDA147A0038B0EB /* AfterSuiteTests+ObjC.m in Sources */ = {isa = PBXBuildFile; fileRef = 64076D241D6D80B500E2B499 /* AfterSuiteTests+ObjC.m */; };
CE175D4E1E8D6B4900EB5E84 /* Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE175D4D1E8D6B4900EB5E84 /* Behavior.swift */; };
CE175D4F1E8D6B4900EB5E84 /* Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE175D4D1E8D6B4900EB5E84 /* Behavior.swift */; };
CE175D501E8D6B4900EB5E84 /* Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE175D4D1E8D6B4900EB5E84 /* Behavior.swift */; };
CE4A578A1EA5DC270063C0D4 /* BehaviorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4A57891EA5DC270063C0D4 /* BehaviorTests.swift */; };
CE4A578E1EA7251C0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4A578D1EA7251C0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift */; };
CE4A57911EA7252E0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4A578D1EA7251C0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift */; };
CE4A57921EA725300063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4A578D1EA7251C0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift */; };
CE4A57931EA725420063C0D4 /* BehaviorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4A57891EA5DC270063C0D4 /* BehaviorTests.swift */; };
CE4A57941EA725440063C0D4 /* BehaviorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4A57891EA5DC270063C0D4 /* BehaviorTests.swift */; };
CE57CEDD1C430BD200D63004 /* NSBundle+CurrentTestBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE57CED81C430BD200D63004 /* NSBundle+CurrentTestBundle.swift */; };
CE57CEDE1C430BD200D63004 /* QuickSelectedTestSuiteBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE57CED91C430BD200D63004 /* QuickSelectedTestSuiteBuilder.swift */; };
CE57CEDF1C430BD200D63004 /* QuickTestSuite.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE57CEDA1C430BD200D63004 /* QuickTestSuite.swift */; };
Expand Down Expand Up @@ -500,6 +509,9 @@
CD261AC81DEC8B0000A8863C /* QuickConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickConfiguration.swift; sourceTree = "<group>"; };
CD3451461E4703D4000C8633 /* QuickMain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickMain.swift; sourceTree = "<group>"; };
CD3451471E4703D4000C8633 /* QuickSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickSpec.swift; sourceTree = "<group>"; };
CE175D4D1E8D6B4900EB5E84 /* Behavior.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Behavior.swift; sourceTree = "<group>"; };
CE4A57891EA5DC270063C0D4 /* BehaviorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BehaviorTests.swift; sourceTree = "<group>"; };
CE4A578D1EA7251C0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FunctionalTests_BehaviorTests_Behaviors.swift; sourceTree = "<group>"; };
CE57CED81C430BD200D63004 /* NSBundle+CurrentTestBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSBundle+CurrentTestBundle.swift"; sourceTree = "<group>"; };
CE57CED91C430BD200D63004 /* QuickSelectedTestSuiteBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickSelectedTestSuiteBuilder.swift; sourceTree = "<group>"; };
CE57CEDA1C430BD200D63004 /* QuickTestSuite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickTestSuite.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -788,6 +800,7 @@
7B5358CA1C3D4E2A00A23FAA /* ContextTests.swift */,
AED9C8621CC8A7BD00432F62 /* CrossReferencingSpecs.swift */,
DED3037C1DF6CF140041394E /* BundleModuleNameTests.swift */,
CE4A57891EA5DC270063C0D4 /* BehaviorTests.swift */,
);
path = FunctionalTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -893,6 +906,7 @@
CE57CEDB1C430BD200D63004 /* URL+FileName.swift */,
34C586071C4AC5E500D4F057 /* ErrorUtility.swift */,
DAEB6B911943873100289F44 /* Supporting Files */,
CE175D4D1E8D6B4900EB5E84 /* Behavior.swift */,
);
name = Quick;
path = Sources/Quick;
Expand Down Expand Up @@ -932,6 +946,7 @@
isa = PBXGroup;
children = (
DA8F91AD19F32CE2006F6675 /* FunctionalTests_SharedExamplesTests_SharedExamples.swift */,
CE4A578D1EA7251C0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift */,
);
path = Fixtures;
sourceTree = "<group>";
Expand Down Expand Up @@ -1442,6 +1457,7 @@
1F118D001BDCA536005013A2 /* Closures.swift in Sources */,
1F118D051BDCA536005013A2 /* ExampleMetadata.swift in Sources */,
1F118D061BDCA536005013A2 /* ExampleGroup.swift in Sources */,
CE175D501E8D6B4900EB5E84 /* Behavior.swift in Sources */,
CE590E1F1C431FE400253D19 /* QuickTestSuite.swift in Sources */,
1F118D091BDCA536005013A2 /* QuickSpec.m in Sources */,
1F118D011BDCA536005013A2 /* ExampleHooks.swift in Sources */,
Expand All @@ -1457,7 +1473,9 @@
1F118D1C1BDCA556005013A2 /* BeforeSuiteTests.swift in Sources */,
1F118D1D1BDCA556005013A2 /* BeforeSuiteTests+ObjC.m in Sources */,
1F118D0E1BDCA547005013A2 /* QCKSpecRunner.m in Sources */,
CE4A57931EA725420063C0D4 /* BehaviorTests.swift in Sources */,
1F118D141BDCA556005013A2 /* FailureTests+ObjC.m in Sources */,
CE4A57911EA7252E0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift in Sources */,
DED3037F1DF6CF140041394E /* BundleModuleNameTests.swift in Sources */,
1F118D0F1BDCA54B005013A2 /* FunctionalTests_SharedExamplesTests_SharedExamples.swift in Sources */,
1F118D101BDCA556005013A2 /* Configuration+AfterEach.swift in Sources */,
Expand Down Expand Up @@ -1521,6 +1539,7 @@
DA3124ED19FCAEE8002858A7 /* World+DSL.swift in Sources */,
DA408BE519FF5599005DF92A /* ExampleHooks.swift in Sources */,
34F375AC19515CA700CE1B99 /* Example.swift in Sources */,
CE175D4F1E8D6B4900EB5E84 /* Behavior.swift in Sources */,
CE590E1A1C431FE300253D19 /* QuickTestSuite.swift in Sources */,
DA3124E719FCAEE8002858A7 /* DSL.swift in Sources */,
DA6B30191A4DB0D500FFB148 /* Filter.swift in Sources */,
Expand All @@ -1542,10 +1561,12 @@
DAB0137019FC4315006AFBEE /* SharedExamples+BeforeEachTests.swift in Sources */,
DA8F91A619F3208B006F6675 /* BeforeSuiteTests.swift in Sources */,
DA8C00221A01E4B900CE58A6 /* QuickConfigurationTests.m in Sources */,
CE4A578A1EA5DC270063C0D4 /* BehaviorTests.swift in Sources */,
DAA63EA419F7637300CD0A3B /* PendingTests.swift in Sources */,
DA8F91AC19F3299E006F6675 /* SharedExamplesTests.swift in Sources */,
DA7AE6F219FC493F000AFDCE /* ItTests.swift in Sources */,
4748E8951A6AEBB3009EC992 /* SharedExamples+BeforeEachTests+ObjC.m in Sources */,
CE4A578E1EA7251C0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift in Sources */,
DA8F91AF19F32CE2006F6675 /* FunctionalTests_SharedExamplesTests_SharedExamples.swift in Sources */,
DAE714FB19FF682A005905B8 /* Configuration+AfterEachTests.swift in Sources */,
AE4E58151C73097C00420A2E /* XCTestObservationCenter+QCKSuspendObservation.m in Sources */,
Expand Down Expand Up @@ -1639,6 +1660,7 @@
7B44ADBE1C5444940007AF2E /* HooksPhase.swift in Sources */,
DA3124EC19FCAEE8002858A7 /* World+DSL.swift in Sources */,
DA408BE419FF5599005DF92A /* ExampleHooks.swift in Sources */,
CE175D4E1E8D6B4900EB5E84 /* Behavior.swift in Sources */,
34F375AB19515CA700CE1B99 /* Example.swift in Sources */,
DA3124E619FCAEE8002858A7 /* DSL.swift in Sources */,
DA6B30181A4DB0D500FFB148 /* Filter.swift in Sources */,
Expand All @@ -1654,7 +1676,9 @@
DA8F919919F31680006F6675 /* QCKSpecRunner.m in Sources */,
DA8940F01B35B1FA00161061 /* FailureUsingXCTAssertTests+ObjC.m in Sources */,
4728253B1A5EECCE008DC74F /* SharedExamplesTests+ObjC.m in Sources */,
CE4A57941EA725440063C0D4 /* BehaviorTests.swift in Sources */,
DAE714F019FF65D3005905B8 /* Configuration+BeforeEachTests.swift in Sources */,
CE4A57921EA725300063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift in Sources */,
DED3037D1DF6CF140041394E /* BundleModuleNameTests.swift in Sources */,
DA05D61019F73A3800771050 /* AfterEachTests.swift in Sources */,
DAB0136F19FC4315006AFBEE /* SharedExamples+BeforeEachTests.swift in Sources */,
Expand Down Expand Up @@ -2609,6 +2633,7 @@
732D8D6C1E516780008558BD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DA5663F31A4C8D8500193C88 /* Build configuration list for PBXNativeTarget "QuickFocused - macOSTests" */ = {
isa = XCConfigurationList;
Expand Down
17 changes: 17 additions & 0 deletions Sources/Quick/Behavior.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
A `Behavior` encapsulates a set of examples that can be re-used in several locations using the `itBehavesLike` function with a context instance of the generic type.
*/

open class Behavior<Context> {

open static var name: String { return String(describing: self) }
/**
override this method in your behavior to define a set of reusable examples.

This behaves just like an example group defines using `describe` or `context`--it may contain any number of `beforeEach`
and `afterEach` closures, as well as any number of examples (defined using `it`).

- parameter aContext: A closure that, when evaluated, returns a `Context` instance that provide the information on the subject.
*/
open class func spec(_ aContext: @escaping () -> Context) {}
}
30 changes: 30 additions & 0 deletions Sources/Quick/DSL/DSL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,22 @@ public func itBehavesLike(_ name: String, flags: FilterFlags = [:], file: String
World.sharedWorld.itBehavesLike(name, sharedExampleContext: sharedExampleContext, flags: flags, file: file, line: line)
}

/**
Inserts the examples defined using a `Behavior` into the current example group.
The shared examples are executed at this location, as if they were written out manually.
This function also passes a strongly-typed context that can be evaluated to give the shared examples extra information on the subject of the example.

- parameter behavior: The type of `Behavior` class defining the example group to be executed.
- parameter context: A closure that, when evaluated, returns an instance of `Behavior`'s context type to provide its example group with extra information on the subject of the example.
- parameter flags: A mapping of string keys to booleans that can be used to filter examples or example groups.
Empty by default.
- parameter file: The absolute path to the file containing the current example group. A sensible default is provided.
- parameter line: The line containing the current example grou 6D4E p. A sensible default is provided.
*/
public func itBehavesLike<C>(_ behavior: Behavior<C>.Type, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, context: @escaping () -> C) {
World.sharedWorld.itBehavesLike(behavior, context: context, flags: flags, file: file, line: line)
}

/**
Defines an example or example group that should not be executed. Use `pending` to temporarily disable
examples or groups that should not be run yet.
Expand Down Expand Up @@ -202,6 +218,13 @@ public func xit(_ description: String, flags: FilterFlags = [:], file: String =
World.sharedWorld.xit(description, flags: flags, file: file, line: line, closure: closure)
}

/**
Use this to quicklu mark an `itBehavesLike` closure as pending.
This disables the example group defined by this behavior and ensures the code within is never run.
*/
public func xitBehavesLike<C>(_ behavior: Behavior<C>.Type, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, context: @escaping () -> C) {
World.sharedWorld.xitBehavesLike(behavior, context: context, flags: flags, file: file, line: line)
}
/**
Use this to quickly focus a `describe` closure, focusing the examples in the closure.
If any examples in the test suite are focused, only those examples are executed.
Expand Down Expand Up @@ -239,3 +262,10 @@ public func fitBehavesLike(_ name: String, flags: FilterFlags = [:], file: Strin
public func fitBehavesLike(_ name: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, sharedExampleContext: @escaping SharedExampleContext) {
World.sharedWorld.fitBehavesLike(name, sharedExampleContext: sharedExampleContext, flags: flags, file: file, line: line)
}

/**
Use this to quickly focus on `itBehavesLike` closure.
*/
public func fitBehavesLike<C>(_ behavior: Behavior<C>.Type, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, context: @escaping () -> C) {
World.sharedWorld.fitBehavesLike(behavior, context: context, flags: flags, file: file, line: line)
}
30 changes: 30 additions & 0 deletions Sources/Quick/DSL/World+DSL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,36 @@ extension World {
self.itBehavesLike(name, sharedExampleContext: sharedExampleContext, flags: focusedFlags, file: file, line: line)
}

internal func itBehavesLike<C>(_ behavior: Behavior<C>.Type, context: @escaping () -> C, flags: FilterFlags, file: String, line: UInt) {
guard currentExampleMetadata == nil else {
raiseError("'itBehavesLike' cannot be used inside '\(currentPhase)', 'itBehavesLike' may only be used inside 'context' or 'describe'. ")
}
let callsite = Callsite(file: file, line: line)
let closure = behavior.spec
let group = ExampleGroup(description: behavior.name, flags: flags)
currentExampleGroup.appendExampleGroup(group)
performWithCurrentExampleGroup(group) {
closure(context)
}

group.walkDownExamples { (example: Example) in
example.isSharedExample = true
example.callsite = callsite
}
}

internal func fitBehavesLike<C>(_ behavior: Behavior<C>.Type, context: @escaping () -> C, flags: FilterFlags, file: String, line: UInt) {
var focusedFlags = flags
focusedFlags[Filter.focused] = true
self.itBehavesLike(behavior, context: context, flags: focusedFlags, file: file, line: line)
}

internal func xitBehavesLike<C>(_ behavior: Behavior<C>.Type, context: @escaping () -> C, flags: FilterFlags, file: String, line: UInt) {
var pendingFlags = flags
pendingFlags[Filter.pending] = true
self.itBehavesLike(behavior, context: context, flags: pendingFlags, file: file, line: line)
}

#if _runtime(_ObjC)
@objc(itWithDescription:flags:file:line:closure:)
private func objc_it(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
Expand Down
11 changes: 10 additions & 1 deletion Tests/QuickTests/QuickFocusedTests/FocusedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ class FunctionalTests_FocusedSpec_SharedExamplesConfiguration: QuickConfiguratio
}
}

class FunctionalTests_FocusedSpec_Behavior: Behavior<Void> {
override static func spec(_ aContext: @escaping () -> Void) {
it("pass once") { expect(true).to(beTruthy()) }
it("pass twice") { expect(true).to(beTruthy()) }
it("pass three times") { expect(true).to(beTruthy()) }
}
}

// The following `QuickSpec`s will be run in a same test suite with other specs
// on SwiftPM. We must avoid that the focused flags below affect other specs, so
// the examples of the two specs must be gathered lastly. That is the reason why
Expand All @@ -28,6 +36,7 @@ class _FunctionalTests_FocusedSpec_Focused: QuickSpec {
}

fitBehavesLike("two passing shared examples")
fitBehavesLike(FunctionalTests_FocusedSpec_Behavior.self) {_ in ()}
}
}

Expand All @@ -54,6 +63,6 @@ final class FocusedTests: XCTestCase, XCTestCaseProvider {
_FunctionalTests_FocusedSpec_Focused.self,
_FunctionalTests_FocusedSpec_Unfocused.self
])
XCTAssertEqual(result?.executionCount, 5 as UInt)
XCTAssertEqual(result?.executionCount, 8 as UInt)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation
import Quick
import Nimble

class FunctionalTests_BehaviorTests_Behavior: Behavior<String> {
override static func spec(_ aContext: @escaping () -> String) {
it("passed the correct parameters via the context") {
let callsite = aContext()
expect(callsite).to(equal("BehaviorSpec"))
}
}
}

class FunctionalTests_BehaviorTests_Behavior2: Behavior<Void> {
override static func spec(_ aContext: @escaping () -> Void) {
it("passes once") { expect(true).to(beTruthy()) }
it("passes twice") { expect(true).to(beTruthy()) }
it("passes three times") { expect(true).to(beTruthy()) }
}
}
59 changes: 59 additions & 0 deletions Tests/QuickTests/QuickTests/FunctionalTests/BehaviorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Foundation
import Quick
import Nimble
import XCTest

class FunctionalTests_BehaviorTests_Spec: QuickSpec {
override func spec() {
itBehavesLike(FunctionalTests_BehaviorTests_Behavior2.self) {_ in ()}
}
}

class FunctionalTests_BehaviorTests_ContextSpec: QuickSpec {
override func spec() {
itBehavesLike(FunctionalTests_BehaviorTests_Behavior.self) {
"BehaviorSpec"
}
}
}

#if _runtime(_ObjC) && !SWIFT_PACKAGE
class FunctionalTests_BehaviorTests_ErrorSpec: QuickSpec {
override func spec() {
describe("error handling when misusing ordering") {
it("should throw an exception when including itBehavesLike in it block") {
expect {
itBehavesLike(FunctionalTests_BehaviorTests_Behavior2.self) { () }
}
.to(raiseException {(exception: NSException) in
expect(exception.name).to(equal(NSExceptionName.internalInconsistencyException))
expect(exception.reason).to(equal("'itBehavesLike' cannot be used inside 'it', 'itBehavesLike' may only be used inside 'context' or 'describe'. "))
})
}
}
}
}
#endif

final class BehaviorTests: XCTestCase, XCTestCaseProvider {

static var allTests: [(String, (BehaviorTests) -> () throws -> Void)] {
return [
("testBehaviorPassContextToExamples",
testBehaviorPassContextToExamples),
("testBehaviorExecutesThreeExamples",
testBehaviorExecutesThreeExamples)
]
}

func testBehaviorExecutesThreeExamples() {
let result = qck_runSpec(FunctionalTests_BehaviorTests_Spec.self)
XCTAssert(result!.hasSucceeded)
XCTAssertEqual(result!.executionCount, 3 as UInt)
}

func testBehaviorPassContextToExamples() {
let result = qck_runSpec(FunctionalTests_BehaviorTests_ContextSpec.self)
XCTAssert(result!.hasSucceeded)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import Nimble
var >
var >

class FunctionalTests_PendingSpec_Behavior: Behavior<Void> {
override static func spec(_ aContext: @escaping () -> Void) {
it("an example that will not run") {
expect(true).to(beFalsy())
}
}
}
class FunctionalTests_PendingSpec: QuickSpec {
override func spec() {
xit("an example that will not run") {
expect(true).to(beFalsy())
}

xitBehavesLike(FunctionalTests_PendingSpec_Behavior.self) {_ in }
describe("a describe block containing only one enabled example") {
beforeEach { oneExampleBeforeEachExecutedCount += 1 }
it("an example that will run") {}
Expand Down
0