Add support for mocking properties. Change placement of file and line parameters.
This commit is contained in:
parent
3b47e1e16a
commit
2d1cd2b11b
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "Cuckoo"
|
||||
s.version = "0.1.1"
|
||||
s.version = "0.3.0"
|
||||
s.summary = "Cuckoo - first boilerplate-free Swift mocking framework."
|
||||
s.description = <<-DESC
|
||||
Cuckoo is a mocking framework with an easy to use API (inspired by Mockito).
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
181C6C2A1C6A67B5009A8CA7 /* TestedClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181C6C281C6A6591009A8CA7 /* TestedClass.swift */; };
|
||||
181F419F1C46C6B3005BAB70 /* StubbingFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181F419E1C46C6B3005BAB70 /* StubbingFunctions.swift */; };
|
||||
181F41A11C46C6E7005BAB70 /* Verification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181F41A01C46C6E7005BAB70 /* Verification.swift */; };
|
||||
181F41A31C46C716005BAB70 /* VerificationFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181F41A21C46C716005BAB70 /* VerificationFunctions.swift */; };
|
||||
|
@ -36,6 +37,7 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
181C6C281C6A6591009A8CA7 /* TestedClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestedClass.swift; sourceTree = "<group>"; };
|
||||
181F419E1C46C6B3005BAB70 /* StubbingFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StubbingFunctions.swift; sourceTree = "<group>"; };
|
||||
181F41A01C46C6E7005BAB70 /* Verification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Verification.swift; sourceTree = "<group>"; };
|
||||
181F41A21C46C716005BAB70 /* VerificationFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerificationFunctions.swift; sourceTree = "<group>"; };
|
||||
|
@ -121,6 +123,7 @@
|
|||
183D040A1C4691C600EBAEF3 /* CuckooAPITest.swift */,
|
||||
183D040C1C4691C600EBAEF3 /* Info.plist */,
|
||||
1894D0F01C4D09AB00879512 /* TestedProtocol.swift */,
|
||||
181C6C281C6A6591009A8CA7 /* TestedClass.swift */,
|
||||
);
|
||||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
|
@ -232,7 +235,7 @@
|
|||
/* Begin PBXShellScriptBuildPhase section */
|
||||
1894D0F31C4D119A00879512 /* Generate mocks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
buildActionMask = 8;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
|
@ -240,9 +243,9 @@
|
|||
name = "Generate mocks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Find the installed Cuckoo version\nCUCKOO_VERSION=\"0.1.0\"\n\n# Used to ignore Xcode's environment\nalias run=\"env -i PATH=$PATH, HOME=$HOME\"\n\ncuckoo runtime --check $CUCKOO_VERSION\ncuckooReturn=$?\n\nif [ $cuckooReturn == 1 ]; then\n# Update local brew repository and upgrade to latest Cuckoo generator\nrun brew update --verbose\nrun brew upgrade --verbose SwiftKit/cuckoo/cuckoo\nelif [ $cuckooReturn == 127 ]; then\n# Update local brew repository and install latest Cuckoo generator\nrun brew install --verbose SwiftKit/cuckoo/cuckoo\nfi\n\ncuckoo generate --runtime $CUCKOO_VERSION --output \"$PROJECT_DIR/Tests/GeneratedMocks.swift\" \"$PROJECT_DIR/Tests/TestedProtocol.swift\"";
|
||||
shellScript = "# Find the installed Cuckoo version\nCUCKOO_VERSION=\"0.1.0\"\n\n# Used to ignore Xcode's environment\nalias run=\"env -i PATH=$PATH, HOME=$HOME\"\n\ncuckoo runtime --check $CUCKOO_VERSION\ncuckooReturn=$?\n\nif [ $cuckooReturn == 1 ]; then\n# Update local brew repository and upgrade to latest Cuckoo generator\nrun brew update --verbose\nrun brew upgrade --verbose SwiftKit/cuckoo/cuckoo\nelif [ $cuckooReturn == 127 ]; then\n# Update local brew repository and install latest Cuckoo generator\nrun brew install --verbose SwiftKit/cuckoo/cuckoo\nfi\n\ncuckoo generate --runtime $CUCKOO_VERSION --output \"$PROJECT_DIR/Tests/GeneratedMocks.swift\" \"$PROJECT_DIR/Tests/TestedProtocol.swift\" \"$PROJECT_DIR/Tests/TestedClass.swift\"";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
|
@ -272,6 +275,7 @@
|
|||
1894D0F21C4D09CF00879512 /* TestedProtocol.swift in Sources */,
|
||||
1894D0F71C4D127D00879512 /* GeneratedMocks.swift in Sources */,
|
||||
1890298B1C4C318A002FF826 /* CuckooAPITest.swift in Sources */,
|
||||
181C6C2A1C6A67B5009A8CA7 /* TestedClass.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,14 @@ enum ReturnValueOrError {
|
|||
case Error(ErrorType)
|
||||
}
|
||||
|
||||
func getterName(property: String) -> String {
|
||||
return property + "#get"
|
||||
}
|
||||
|
||||
func setterName(property: String) -> String {
|
||||
return property + "#set"
|
||||
}
|
||||
|
||||
public class MockManager<STUBBING: StubbingProxy, VERIFICATION: VerificationProxy> {
|
||||
private var stubs: [String: [Stub]] = [:]
|
||||
private var stubCalls: [StubCall] = []
|
||||
|
@ -61,7 +69,7 @@ public class MockManager<STUBBING: StubbingProxy, VERIFICATION: VerificationProx
|
|||
stubs[stub.name]?.insert(stub, atIndex: 0)
|
||||
}
|
||||
|
||||
private func verify(method: String, file: String, line: UInt, callMatcher: AnyMatcher<StubCall>, verificationMatcher: AnyMatcher<[StubCall]>) {
|
||||
private func verify(method: String, sourceLocation: SourceLocation, callMatcher: AnyMatcher<StubCall>, verificationMatcher: AnyMatcher<[StubCall]>) {
|
||||
let calls = stubCalls.filter(callMatcher.matches)
|
||||
|
||||
if verificationMatcher.matches(calls) == false {
|
||||
|
@ -72,7 +80,7 @@ public class MockManager<STUBBING: StubbingProxy, VERIFICATION: VerificationProx
|
|||
.appendText(", but ");
|
||||
verificationMatcher.describeMismatch(calls, to: description);
|
||||
|
||||
XCTFail(description.description, file: file, line: line)
|
||||
XCTFail(description.description, file: sourceLocation.file, line: sourceLocation.line)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -88,13 +96,21 @@ extension MockManager {
|
|||
|
||||
extension MockManager {
|
||||
|
||||
public func getVerificationProxy(matcher: AnyMatcher<[StubCall]>) -> VERIFICATION {
|
||||
return VERIFICATION(handler: VerificationHandler(matcher: matcher, verifyCall: verify))
|
||||
public func getVerificationProxy(matcher: AnyMatcher<[StubCall]>, sourceLocation: SourceLocation) -> VERIFICATION {
|
||||
return VERIFICATION(handler: VerificationHandler(matcher: matcher, sourceLocation: sourceLocation, verifyCall: verify))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension MockManager {
|
||||
public func getter<T>(property: String, original: (Void -> T)? = nil) -> (Void -> T) {
|
||||
return call(getterName(property), original: original)
|
||||
}
|
||||
|
||||
public func setter<T>(property: String, value: T, original: (T -> Void)? = nil) -> (T -> Void) {
|
||||
return call(setterName(property), parameters: value, original: original)
|
||||
}
|
||||
|
||||
public func call<OUT>(method: String, original: (Void -> OUT)? = nil) -> Void -> OUT {
|
||||
return doCall(method, parameters: Void(), original: original)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<string>0.3.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
|
|
@ -43,9 +43,41 @@ public struct ToBeStubbedThrowingFunction<IN, OUT> {
|
|||
}
|
||||
}
|
||||
|
||||
public struct ToBeStubbedReadOnlyProperty<T> {
|
||||
let handler: StubbingHandler
|
||||
|
||||
let name: String
|
||||
|
||||
public var get: ToBeStubbedFunction<Void, T> {
|
||||
return ToBeStubbedFunction(handler: handler, name: getterName(name), parameterMatchers: [])
|
||||
}
|
||||
}
|
||||
|
||||
public struct ToBeStubbedProperty<T> {
|
||||
let handler: StubbingHandler
|
||||
|
||||
let name: String
|
||||
|
||||
public var get: ToBeStubbedFunction<Void, T> {
|
||||
return ToBeStubbedFunction(handler: handler, name: getterName(name), parameterMatchers: [])
|
||||
}
|
||||
|
||||
public func set<M: Matchable where M.MatchedType == T>(matcher: M) -> ToBeStubbedFunction<T, Void> {
|
||||
return ToBeStubbedFunction(handler: handler, name: setterName(name), parameterMatchers: [matcher.matcher])
|
||||
}
|
||||
}
|
||||
|
||||
public struct StubbingHandler {
|
||||
let createNewStub: Stub -> ()
|
||||
|
||||
public func stubProperty<T>(property: String) -> ToBeStubbedProperty<T> {
|
||||
return ToBeStubbedProperty(handler: self, name: property)
|
||||
}
|
||||
|
||||
public func stubReadOnlyProperty<T>(property: String) -> ToBeStubbedReadOnlyProperty<T> {
|
||||
return ToBeStubbedReadOnlyProperty(handler: self, name: property)
|
||||
}
|
||||
|
||||
public func stub<OUT>(method: String) -> ToBeStubbedFunction<Void, OUT> {
|
||||
return stub(method, parameterMatchers: [] as [AnyMatcher<Void>])
|
||||
}
|
||||
|
|
|
@ -59,3 +59,8 @@ public func markerFunction<IN, OUT>(input: IN.Type = IN.self, _ output: OUT.Type
|
|||
return OUT.self as! OUT
|
||||
}
|
||||
}
|
||||
|
||||
public struct SourceLocation {
|
||||
let file: String
|
||||
let line: UInt
|
||||
}
|
|
@ -12,18 +12,27 @@ public protocol VerificationProxy {
|
|||
|
||||
public struct VerificationHandler {
|
||||
let matcher: AnyMatcher<[StubCall]>
|
||||
let verifyCall: (method: String, file: String, line: UInt, callMatcher: AnyMatcher<StubCall>, verificationMatcher: AnyMatcher<[StubCall]>) -> ()
|
||||
let sourceLocation: SourceLocation
|
||||
let verifyCall: (method: String, sourceLocation: SourceLocation, callMatcher: AnyMatcher<StubCall>, verificationMatcher: AnyMatcher<[StubCall]>) -> ()
|
||||
|
||||
public func verify<OUT>(method: String, file: String, line: UInt) -> __DoNotUse<OUT> {
|
||||
return verify(method, file: file, line: line, parameterMatchers: [] as [AnyMatcher<Void>])
|
||||
public func verifyProperty<T>(property: String) -> VerifyProperty<T> {
|
||||
return VerifyProperty(name: property, handler: self)
|
||||
}
|
||||
|
||||
public func verify<IN, OUT>(method: String, file: String, line: UInt, parameterMatchers: [AnyMatcher<IN>]) -> __DoNotUse<OUT> {
|
||||
return verify(method, file: file, line: line, callMatcher: callMatcher(method, parameterMatchers: parameterMatchers))
|
||||
public func verifyReadOnlyProperty<T>(property: String) -> VerifyReadOnlyProperty<T> {
|
||||
return VerifyReadOnlyProperty(name: property, handler: self)
|
||||
}
|
||||
|
||||
public func verify<OUT>(method: String, file: String, line: UInt, callMatcher: AnyMatcher<StubCall>) -> __DoNotUse<OUT> {
|
||||
verifyCall(method: method, file: file, line: line, callMatcher: callMatcher, verificationMatcher: matcher)
|
||||
public func verify<OUT>(method: String) -> __DoNotUse<OUT> {
|
||||
return verify(method, parameterMatchers: [] as [AnyMatcher<Void>])
|
||||
}
|
||||
|
||||
public func verify<IN, OUT>(method: String, parameterMatchers: [AnyMatcher<IN>]) -> __DoNotUse<OUT> {
|
||||
return verify(method, callMatcher: callMatcher(method, parameterMatchers: parameterMatchers))
|
||||
}
|
||||
|
||||
public func verify<OUT>(method: String, callMatcher: AnyMatcher<StubCall>) -> __DoNotUse<OUT> {
|
||||
verifyCall(method: method, sourceLocation: sourceLocation, callMatcher: callMatcher, verificationMatcher: matcher)
|
||||
return __DoNotUse()
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +40,29 @@ public struct VerificationHandler {
|
|||
/// Marker struct for use as a return type in verification.
|
||||
public struct __DoNotUse<T> { }
|
||||
|
||||
public struct VerifyReadOnlyProperty<T> {
|
||||
let name: String
|
||||
let handler: VerificationHandler
|
||||
|
||||
public var get: __DoNotUse<T> {
|
||||
return handler.verify(getterName(name))
|
||||
}
|
||||
}
|
||||
|
||||
public struct VerifyProperty<T> {
|
||||
let name: String
|
||||
let handler: VerificationHandler
|
||||
|
||||
public var get: __DoNotUse<T> {
|
||||
return handler.verify(getterName(name))
|
||||
}
|
||||
|
||||
public func set<M: Matchable where M.MatchedType == T>(matcher: M) -> __DoNotUse<Void> {
|
||||
return handler.verify(setterName(name), parameterMatchers: [matcher.matcher])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public func parameterMatcher<IN, PARAM, M: Matcher where M.MatchedType == PARAM>(matcher: M, mapping: IN -> PARAM) -> AnyMatcher<IN> {
|
||||
let function: IN -> Bool = {
|
||||
return matcher.matches(mapping($0))
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
//
|
||||
|
||||
@warn_unused_result
|
||||
public func verify<M: Mock>(mock: M) -> M.Verification {
|
||||
return verify(mock, times(1))
|
||||
public func verify<M: Mock>(mock: M, file: String = __FILE__, line: UInt = __LINE__) -> M.Verification {
|
||||
return verify(mock, times(1), file: file, line: line)
|
||||
}
|
||||
|
||||
@warn_unused_result
|
||||
public func verify<M: Mock>(mock: M, _ matcher: AnyMatcher<[StubCall]>) -> M.Verification {
|
||||
return mock.manager.getVerificationProxy(matcher)
|
||||
public func verify<M: Mock>(mock: M, _ matcher: AnyMatcher<[StubCall]>, file: String = __FILE__, line: UInt = __LINE__) -> M.Verification {
|
||||
return mock.manager.getVerificationProxy(matcher, sourceLocation: SourceLocation(file: file, line: line))
|
||||
}
|
|
@ -21,7 +21,7 @@ class MockeryAPITest: XCTestCase {
|
|||
super.tearDown()
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
func testProtocol() {
|
||||
|
||||
enum TestError: ErrorType {
|
||||
case Unknown
|
||||
|
@ -32,6 +32,12 @@ class MockeryAPITest: XCTestCase {
|
|||
// FIXME Should be fatalError when method was not throwing
|
||||
|
||||
stub(mock) { mock in
|
||||
when(mock.readOnlyProperty.get).thenReturn("properties!")
|
||||
when(mock.readWriteProperty.get).thenReturn(10)
|
||||
when(mock.readWriteProperty.set(anyInt())).then {
|
||||
print($0)
|
||||
}
|
||||
|
||||
when(mock.noParameter()).thenReturn()
|
||||
when(mock.countCharacters("hello")).thenReturn(1000)
|
||||
when(mock.withReturn()).thenReturn("hello world!")
|
||||
|
@ -42,6 +48,11 @@ class MockeryAPITest: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
XCTAssertEqual(mock.readOnlyProperty, "properties!")
|
||||
XCTAssertEqual(mock.readWriteProperty, 10)
|
||||
mock.readWriteProperty = 400
|
||||
XCTAssertEqual(mock.readWriteProperty, 10)
|
||||
|
||||
mock.noParameter()
|
||||
|
||||
XCTAssertEqual(mock.countCharacters("hello"), 1000)
|
||||
|
@ -54,12 +65,62 @@ class MockeryAPITest: XCTestCase {
|
|||
}
|
||||
XCTAssertEqual(helloWorld, "hello world")
|
||||
|
||||
verify(mock).readOnlyProperty.get
|
||||
verify(mock, times(2)).readWriteProperty.get
|
||||
verify(mock).readWriteProperty.set(400)
|
||||
verify(mock).noParameter()
|
||||
|
||||
verify(mock).countCharacters(eq("hello"))
|
||||
|
||||
verify(mock).withReturn()
|
||||
verify(mock, never()).withThrows()
|
||||
}
|
||||
|
||||
func testClass() {
|
||||
enum TestError: ErrorType {
|
||||
case Unknown
|
||||
}
|
||||
|
||||
let mock = MockTestedClass()
|
||||
|
||||
stub(mock) { mock in
|
||||
when(mock.readOnlyProperty.get).thenReturn("properties!")
|
||||
when(mock.readWriteProperty.get).thenReturn(10)
|
||||
when(mock.readWriteProperty.set(anyInt())).then {
|
||||
print($0)
|
||||
}
|
||||
|
||||
when(mock.noParameter()).thenReturn()
|
||||
when(mock.countCharacters("hello")).thenReturn(1000)
|
||||
when(mock.withReturn()).thenReturn("hello world!")
|
||||
when(mock.withThrows()).thenThrow(TestError.Unknown)
|
||||
|
||||
when(mock.withNoescape("hello", closure: anyClosure())).then {
|
||||
$1($0 + " world")
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertEqual(mock.readOnlyProperty, "properties!")
|
||||
XCTAssertEqual(mock.readWriteProperty, 10)
|
||||
mock.readWriteProperty = 400
|
||||
XCTAssertEqual(mock.readWriteProperty, 10)
|
||||
|
||||
mock.noParameter()
|
||||
|
||||
XCTAssertEqual(mock.countCharacters("hello"), 1000)
|
||||
|
||||
XCTAssertEqual(mock.withReturn(), "hello world!")
|
||||
|
||||
var helloWorld: String = ""
|
||||
mock.withNoescape("hello") {
|
||||
helloWorld = $0
|
||||
}
|
||||
XCTAssertEqual(helloWorld, "hello world")
|
||||
|
||||
verify(mock).readOnlyProperty.get
|
||||
verify(mock, times(2)).readWriteProperty.get
|
||||
verify(mock).readWriteProperty.set(400)
|
||||
verify(mock).noParameter()
|
||||
verify(mock).countCharacters(eq("hello"))
|
||||
verify(mock).withReturn()
|
||||
verify(mock, never()).withThrows()
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<string>0.3.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// TestedClass.swift
|
||||
// Cuckoo
|
||||
//
|
||||
// Created by Tadeas Kriz on 09/02/16.
|
||||
// Copyright © 2016 Brightify. All rights reserved.
|
||||
//
|
||||
|
||||
class TestedClass {
|
||||
let constant: Float = 0.0
|
||||
|
||||
var readOnlyProperty: String {
|
||||
return ""
|
||||
}
|
||||
|
||||
lazy var readWriteProperty: Int = 0
|
||||
|
||||
func noParameter() {
|
||||
|
||||
}
|
||||
|
||||
func countCharacters(test: String) -> Int {
|
||||
return test.characters.count
|
||||
}
|
||||
|
||||
func withReturn() -> String {
|
||||
return "yello world"
|
||||
}
|
||||
|
||||
func withThrows() throws {
|
||||
|
||||
}
|
||||
|
||||
func withClosure(closure: String -> Int) {
|
||||
closure("hello")
|
||||
}
|
||||
|
||||
func withMultipleParameters(a: String, b: Int, c: Float) {
|
||||
|
||||
}
|
||||
|
||||
func withNoescape(a: String, @noescape closure: String -> Void) {
|
||||
closure(a)
|
||||
}
|
||||
}
|
|
@ -7,6 +7,10 @@
|
|||
//
|
||||
|
||||
protocol TestedProtocol {
|
||||
var readOnlyProperty: String { get }
|
||||
|
||||
var readWriteProperty: Int { get set }
|
||||
|
||||
func noParameter()
|
||||
|
||||
func countCharacters(test: String) -> Int
|
||||
|
|
Loading…
Reference in New Issue