trivago / dobby Goto Github PK
View Code? Open in Web Editor NEWSwift helpers for mocking and stubbing
License: Apache License 2.0
Swift helpers for mocking and stubbing
License: Apache License 2.0
Strict mocks should fail fast.
Add expectations and matchers for Nimble. We already use something like this internally:
func verify<Interaction: Equatable>(mock: Mock<Interaction>, file: String = __FILE__, line: UInt = __LINE__) -> Expectation<Mock<Interaction>> {
return expect(mock)
}
func contain<Interaction: Equatable>(interactions: Interaction...) -> NonNilMatcherFunc<Mock<Interaction>> {
return NonNilMatcherFunc { actualExpression, failureMessage in
failureMessage.postfixMessage = "contain <\(interactions)>"
if let actual = actualExpression.evaluate() {
return Dobby.verify(actual, interactions)
}
return false
}
}
However, failure messages could be improved and we might want to discuss #2 before moving on here.
It still has 0.6.0 in it. Thus 0.7.0 is not available via regular notation.
I started working on a playground for fun and the Düsseldorf iOS Meetup some time ago. It's very useful for trying out things, especially when someone is new to the framework. I think we should provide one with some documentation and useful examples.
mock.expect(matches([10, 20]))
produces xcode error.
Xcode 9.3, swift 4.1
Maybe it would be possible to provide real mocks using swizzling for NSObject
subclasses?
Can anyone help with this?
When I do an update it falls over when building Dobby. Other projects are fine.
*** Building scheme "Dobby-iOS" in Dobby.xcworkspace
** BUILD FAILED **
The following build commands failed:
Ld /Users/scott/Library/Developer/Xcode/DerivedData/Dobby-ecuwfbyesepmhtfviqolgxllubry/Build/Intermediates/Dobby.build/Release-iphoneos/Dobby-iOS.build/Objects-normal/arm64/Dobby normal arm64
Ld /Users/scott/Library/Developer/Xcode/DerivedData/Dobby-ecuwfbyesepmhtfviqolgxllubry/Build/Intermediates/Dobby.build/Release-iphoneos/Dobby-iOS.build/Objects-normal/armv7/Dobby normal armv7
ld: warning: Auto-Linking supplied '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework/XCTest.tbd', bitcode bundle could not be generated because '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework/XCTest.tbd' was built without full bitcode. All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build
ld: warning: Auto-Linking supplied '/Applications/Xcode.app/Contents/Developer/Toolchains/Swift_2.3.xctoolchain/usr/lib/swift/iphoneos/libswiftXCTest.dylib', '/Applications/Xcode.app/Contents/Developer/Toolchains/Swift_2.3.xctoolchain/usr/lib/swift/iphoneos/libswiftXCTest.dylib' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ld: warning: Auto-Linking supplied '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework/XCTest.tbd', bitcode bundle could not be generated because '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework/XCTest.tbd' was built without full bitcode. All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build
ld: warning: Auto-Linking supplied '/Applications/Xcode.app/Contents/Developer/Toolchains/Swift_2.3.xctoolchain/usr/lib/swift/iphoneos/libswiftXCTest.dylib', '/Applications/Xcode.app/Contents/Developer/Toolchains/Swift_2.3.xctoolchain/usr/lib/swift/iphoneos/libswiftXCTest.dylib' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.
clang: error: linker command failed with exit code 1 (use -v to see invocation)
A shell task (/usr/bin/xcrun xcodebuild -workspace /Volumes/Mobi/Mobi2GoInStoreManager/Carthage/Checkouts/Dobby/Dobby.xcworkspace -scheme Dobby-iOS -configuration Release -sdk iphoneos ONLY_ACTIVE_ARCH=NO BITCODE_GENERATION_MODE=bitcode CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES clean build) failed with exit code 65:
** BUILD FAILED **
Hi,
Related to my response in #34, I noticed that building Dobby @0.7.0 results in this warning (from Xcode or via Carthage integration):
ld: warning: linking against a dylib which is not safe for use in application extensions: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/XCTest.framework/XCTest.tbd
ld: warning: linking against a dylib which is not safe for use in application extensions: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator/libswiftXCTest.dylib
Looking into this further, Dobby sets APPLICATION_EXTENSION_API_ONLY = YES
via the Configuration/Base/Targets/Frameworks.xcconfig
configuration file.
// Disallows use of APIs that are not available
// to app extensions and linking to frameworks
// that have not been built with this setting enabled.
APPLICATION_EXTENSION_API_ONLY = YES
While this probably makes sense for most frameworks, does it make sense for Dobby? The warning seems to stem from the fact that Dobby links/requires XCTest
which itself doesn't seem to have been built with this setting enabled.
I've tested that overriding that setting for the Dobby-iOS
target removes the warnings. I kind of think that Dobby as a unit-test framework that links against XCTest shouldn't disallow the use of APIs that are not available to app extensions. Thoughts?
I can open a PR with this change if you agree: master...mdarnall:dont-require-app-extension-apis
Circle CI?
Mock
s and Stub
s with Void
interactions require some unnecessary typing, like:
let stub: Stub<(), Int> = Stub()
stub.on(any(), returnValue: 42)
We could add appropriate protocols, MockType
and StubType
, and extensions for where Interaction == Void
to provide:
let stub: Stub<(), Int> = Stub()
stub.returnValue(42)
This is basically about improving support for Optional<T> where T: Equatable
, meaning it should be possible to expect "foobar"
for String?
—instead of matches { $0 == "foobar" }
—and maybe simplify expecting nil
.
After the recent project transfer from Rheinfabrik to trivago (see this), I'm wondering whether we should also update the product bundle identifier and the organization name (probably)? Any objections from your side, @tibr?
Working on my unit tests I faced an explicit error:
Dobby.StubError<(Swift.String, Swift.String, *******.Result<()> -> ()), ()>.UnexpectedInteraction("slug", "script", (Function))
The thing is, I already mock-ed a lot of methods looking pretty much the same without any trouble.
Relevant classes / enum:
public enum Result<T> {
case Success(T)
case Error(ErrorType)
}
public class MockExecutionHandler : ExecutionHandlerOperations {
public let startMock = Mock<(String, String, (Result<Void>) -> ())>()
public let startStub = Stub<(String, String, (Result<Void>) -> ()), Void>()
public func start(slug: String, script: String, completion: (Result<Void>) -> ()) {
startMock.record((slug, script, completion))
try! startStub.invoke(slug, script, completion)
}
}
Test mock:
handlerMock.startStub.on(matches((self.slug, self.script, any()))) {
slug, script, completion in completion(Result.Success(())) // Problem?
}
handlerMock.startMock.expect(matches((self.slug, self.script, any())))
Executed code:
handler.start(crop.getSlug(), script: subscribedCrop.getScript()) {
startResult in
switch startResult {
case .Success: completion(Result.Success(crop)) // case Result.Success(Void): Do something else
case .Error(let error): completion(Result.Error(error))
}
}
As you can see, my stub should be typed as a Result<()> -> ()
, instead of that I've got a (Function)
. Is it a bug or I am doing something wrong ?
PS: If I don't mock my ExecutionHandler and implement start this way the test passes:
public func start(slug: String, script: String, completion: (Result<Void>) -> ()) {
completion(Result.Success())
}
Note: The code below is a working example of a similar code.
public let fetchAvailableCropMock = Mock<(Set<Sensor>?, (Result<Array<Crop>>) -> ())>()
public let fetchAvailableCropStub = Stub<(Set<Sensor>?, (Result<Array<Crop>>) -> ()), Void>()
public func fetchAvailableCrop(availableSensors: Set<Sensor>?, completion: (Result<Array<Crop>>) -> ()) {
fetchAvailableCropMock.record((availableSensors, completion))
try! fetchAvailableCropStub.invoke(availableSensors, completion)
}
And in the passing test:
self.storeClientMock().fetchAvailableCropStub.on(matches((any(), any()))) {
sensors, completion in completion(Result.Success([self.crop]))
}
self.storeClientMock().fetchAvailableCropMock.expect(matches((any(),any())))
I don't see any relevant difference. Any idea ?
Am I missing something obvious ?
Allowing expectations to be fulfilled out of order might be useful.
The program flow leading to an expectation being fulfilled is not always observable, thus cannot always be waited for. Retrying verification within a certain amount of time allows those interactions to happen.
Mock
is always strict, being a little nice could be useful sometimes.
Give a nice and ordered mock with two expectations, one negative and one positive (in that order), then verification will never get past the negative expectation. For example:
let mock = Mock<Int>(nice: true, ordered: true)
mock.reject(1)
mock.expect(2)
mock.record(2)
mock.verify()
The above example should successfully verify, but it fails with the following message:
Expectation <2> not fulfilled
Although not needed for strict mocks, on nice mocks—see #16—, being able to reject interactions should be useful.
How about including mocks for common iOS classes like NSURLSession
?
verify
currently behaves like contains
, meaning all expected interactions must have been recorded, other interactions (before, in-between, after) are ok. This could potentially be confusing. Any opinions?
When calling
behave(stub, interaction(), foo)
behave(stub, interaction(), bar)
the stub will always return foo
. I would have expected it to return bar
. Additionally, we could log a warning that informs the user about multiple behaviors for the same interaction and the resulting return value.
My current workaround looks like this:
behave(stub, interaction(), foo)
stub.behavior = []
behave(stub, interaction(), bar)
Maybe we should also add a more obvious way to reset behaviors for a stub?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.