I'm trying to inject a fake instance into a unit test for a class depending on SimplePing, a NSObject subclass. My class has a property var simplePings: [SimplePing] which in my unit test I set as an array of FakeSimplePing. However, when the class goes in the array and calls simplePing.start(), it calls the SimplePing.start implementation instead of FakeSimplePing's, even though when I debug I see that the instance type is FakeSimplePing.
When the property is just a single SimplePing, the unit test uses the FakeSimplePing.start and the test passes. Does this have something to do with Swift and arrays of superclasses?
class Pinger : NSObject {
private var simplePings: [SimplePing] = []
func pingLocation(location: Location) -> Signal<Double, NoError> {
let simplePings = location.serverIPs.map { (serverIP: String) -> SimplePing in
let simplePing = SimplePing(hostName: serverIP)
simplePing?.delegate = self
return simplePing
}
configureDependencies(simplePings)
simplePings.forEach { $0.start() }
return signal
}
func configureDependencies(simplePings: [SimplePing]) {
if self.simplePings.isEmpty {
self.simplePings = simplePings
}
}
}
class FakeSimplePing: SimplePing {
var receivedStart = false
var receivedSendPingWithData = false
var fakeHostName: String!
override var hostName: String {
return fakeHostName
}
convenience init(hostName: String) {
self.init()
fakeHostName = hostName
}
override func start() {
// This does not get called
receivedStart = true
delegate?.simplePing?(self, didStartWithAddress: nil)
delegate?.simplePing?(self, didReceivePingResponsePacket: nil)
}
override func sendPingWithData(data: NSData!) {
receivedSendPingWithData = true
}
}
And the failing test:
beforeEach {
fakeSimplePing = FakeSimplePing(hostName: serverIP)
fakeSimplePing.delegate = pinger
pinger.configureDependencies([fakeSimplePing])
}
it("pings server with data") {
pinger.pingLocation(location)
expect(fakeSimplePing.receivedSendPingWithData).toEventually(beTrue())
}
The problem (I believe...) is in the naming in pingLocation
in the line
let simplePings = location.serverIPs.map { ....
you use the same name as of your property
private var simplePings: [SimplePing] = []
so you may think you're defining a new variable with the let, but actually, you may just use your property and change it on the way, so it got changed to SimplePing array, as it returns from the map
try to change your method into:
func pingLocation(location: Location) -> Signal<Double, NoError> {
let tempSimplePings = location.serverIPs.map { (serverIP: String) -> SimplePing in
let simplePing = SimplePing(hostName: serverIP)
simplePing?.delegate = self
return simplePing
}
configureDependencies(tempSimplePings)
simplePings.forEach { $0.start() }
return signal
}
Related
this is my test :
class ZabiTests: FBSnapshotTestCase {
var vc:UIViewController?
override func setUpWithError() throws {
super.setUp()
vc = ViewController()
recordMode = true
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
FBSnapshotVerifyView((vc?.view)!)
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
first time i run with recordMode = true and then i run without this line, and still i get Snapshot comparison failed
(vc is okey, red background and not nil )
After playing around with it I created an alternative way of auto defining those folders by creating my own base class:
open class MySnapshotTestCase: FBSnapshotTestCase {
var referenceImageDirectory: String?
var imageDiffDirectory: String?
public func verify(
view: UIView,
file: StaticString = #file,
line: UInt = #line
) {
let directory = ("\(file)" as NSString).deletingLastPathComponent
// Save for later use in getReferenceImageDirectory
referenceImageDirectory = NSString.path(withComponents: [directory, "ReferenceImages"])
// Save for later use in getImageDiffDirectory
imageDiffDirectory = NSString.path(withComponents: [directory, "FailedSnapshotTests"])
FBSnapshotVerifyView(
view,
identifier: nil,
perPixelTolerance: 0,
overallTolerance: 0,
file: file,
line: line)
}
open override func getReferenceImageDirectory(withDefault dir: String?) -> String {
guard let referenceImageDirectory = referenceImageDirectory else {
fatalError("Do not call FBSnapshotVerifyView directly, use WazeSnapshotTestCase.verify instead.")
}
return referenceImageDirectory
}
open override func getImageDiffDirectory(withDefault dir: Swift.String?) -> Swift.String {
guard let imageDiffDirectory = imageDiffDirectory else {
fatalError("Do not call FBSnapshotVerifyView directly, use WazeSnapshotTestCase.verify instead.")
}
return imageDiffDirectory
}
}
This is what I need for test purposes:
class AssemblerMock: Assemblerable {
func resolve<Service>(_ serviceType: Service.Type) -> Service? {
return Service.init() //doesnt work, need to return non nil value here.
}
}
It works with a little workaround: You need to create a protocol, let's call it Initable:
protocol Initable {
init()
}
then, your resolve-Template-Method should require Service to be Initable:
func resolve<Service>(_ serviceType: Service.Type) -> Service where Service:Initable {
return Service.init()
}
Before using it, you also need to create an extension to all the types you might want to resolve:
extension Int : Initable {
// No implementation possible/needed, because `init` already exits in struct Int
}
and then call it:
let am = AssemblerMock()
let i = am.resolve(Int.self)
print (i) // Prints "0" because this is the default Integer value
Remark: I made the return type to return Service and not Service?, but it doesn't matter here. If you want to support failable initializers (init?), you need to modify the return type as well as the Initable protocol:
protocol Initable {
init?()
}
extension Int : Initable {}
class FooFailing : Initable {
required init?() {
return nil
}
}
class AssemblerMock {
func resolve<Service>(_ serviceType: Service.Type) -> Service? where Service:Initable {
return Service.init()
}
}
let am = AssemblerMock()
let i = am.resolve(Int.self)
print (i) // Optional(0)
let foo = am.resolve(FooFailing.self)
print (foo) // nil
I have a method that loads an array of dictionaries from a propertylist. Then I change those arrays of dictionaries to array of a defined custom type;
I want to write that method in generic form so I call that method with the type I expect, then the method loads it and returns an array of my custom type rather than dictionaries
func loadPropertyList(fileName: String) -> [[String:AnyObject]]?
{
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "plist")
{
if let plistXML = NSFileManager.defaultManager().contentsAtPath(path)
{
do {
if let temp = try NSPropertyListSerialization.propertyListWithData(plistXML, options: .Immutable, format: nil) as? [[String:AnyObject]]
{
return temp
}
}catch{}
}
}
return nil
}
//
func loadList<T>(fileName: String) -> [T]?{//**Here the answer I am expecting**}
I am assuming your function to read from a Plist works and that you don't want to subclass NSObject.
Since Swift reflecting does not support setting values this is not possible without some implementation for each Type you want this to work for.
It can however be done in a pretty elegant way.
struct PlistUtils { // encapsulate everything
static func loadPropertyList(fileName: String) -> [[String:AnyObject]]? {
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "plist") {
if let plistXML = NSFileManager.defaultManager().contentsAtPath(path) {
do {
if let temp = try NSPropertyListSerialization.propertyListWithData(plistXML, options: .Immutable, format: nil) as? [[String:AnyObject]] {
return temp
}
} catch {
return nil
}
}
}
return nil
}
}
This protocol will be used in a generic fashion to get the Type name and read the corresponding Plist.
protocol PListConstructible {
static func read() -> [Self]
}
This protocol will be used to implement Key Value setters.
protocol KeyValueSettable {
static func set(fromKeyValueStore values:[String:AnyObject]) -> Self
}
This is the combination of both to generate an array of objects. This does require that the Plist is named after the Type.
extension PListConstructible where Self : KeyValueSettable {
static func read() -> [Self] {
let name = String(reflecting: self)
var instances : [Self] = []
if let data = PlistUtils.loadPropertyList(name) {
for entry in data {
instances.append(Self.set(fromKeyValueStore: entry))
}
}
return instances
}
}
This is some Type.
struct Some : PListConstructible {
var alpha : Int = 0
var beta : String = ""
}
All you have to do is implement the Key Value setter and it will now be able to be read from a Plist.
extension Some : KeyValueSettable {
static func set(fromKeyValueStore values: [String : AnyObject]) -> Some {
var some = Some()
some.alpha = (values["alpha"] as? Int) ?? some.alpha
some.beta = (values["beta"] as? String) ?? some.beta
return some
}
}
This is how you use it.
Some.read()
How can I check if a property is an Array (of any type)? This code always only prints "Worker". Is there a way (dynamically) to know if a property is an Array without inform the type?
final class Worker: NSObject {
var id: Int?
var array: Array<Worker>?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let worker = Worker()
worker.id = Int(2) as Int?
worker.array = [Worker(),Worker(),Worker()]
let mirror = reflect(worker)
for i in 0..<mirror.count {
let (name, childMirror) = mirror[i]
if childMirror.disposition == .Optional {
let (newName,subChildMirror) = childMirror[0]
if subChildMirror.valueType is Array<AnyClass>.Type {
println("AnyClass")
}
if subChildMirror.valueType is Array<AnyObject>.Type {
println("AnyObject")
}
if subChildMirror.valueType is Array<Any>.Type {
println("Any")
}
if subChildMirror.valueType is Array<NSObject>.Type {
println("NSObject")
}
if subChildMirror.valueType is Array<Worker>.Type {
println("Worker")
}
}
}
}
}
Ps.: I need to deal with Array<>
An array of any type can always be casted to a NSArray. So you could check if it's an array with code like this:
if _ = subChildMirror.valueType as? NSArray {
println("Array")
}
It's also possible to dynamically get the type of the objects of that array. In my EVReflection library I do something similar. I extended the Array in order to get a new element of an object what should be in that Array. In your case you could then get the .dynamicType from dat object.
So the code would become:
let arrayType = worker.array.getTypeInstance().dynamicType
Here is the Array extension
extension Array {
/**
Get the type of the object where this array is for
:returns: The object type
*/
public func getTypeInstance<T>(
) -> T {
let nsobjectype : NSObject.Type = T.self as! NSObject.Type
let nsobject: NSObject = nsobjectype.init()
return nsobject as! T
}
}
How to access to static protocol method within a instance
I have a list of Contact, the contact can be a FamilyContact that inherit from Contact and the GroupStatus protocol
I want to call the static method from GroupStatus but in vain...
Here is my code
protocol GroupStatus {
static func isPrivate() -> Bool // static method that indicates the status
}
protocol IsBusy {
func wizzIt()
}
class AdresseBook {
private var contacts = [Contact]()
func addOne(c: Contact) {
contacts.append(c)
}
func listNonPrivated() -> [Contact]? {
var nonPrivateContact = [Contact]()
for contact in contacts {
// here is I should call the static method provided by the protocol
if self is GroupStatus {
let isPrivate = contact.dynamicType.isPrivate()
if !isPrivate {
nonPrivateContact.append(contact)
}
}
nonPrivateContact.append(contact)
}
return nonPrivateContact
}
}
class Contact : Printable {
var name: String
init(name: String) {
self.name = name
}
func wizz() -> Bool {
if let obj = self as? IsBusy {
obj.wizzIt()
return true
}
return false
}
var description: String {
return self.name
}
}
class FamilyContact: Contact, GroupStatus {
static func isPrivate() -> Bool {
return true
}
}
I can't compile Contact.Type does not have a member named 'isPrivate'
How can I call it ? It works if I delete the static keyword, but I think is more logical to define it static.
If I replace
let isPrivate = contact.dynamicType.isPrivate()
by
let isPrivate = FamilyContact.isPrivate()
It works, but I can have more than 1 subclasses
If I remove the static keywork I can do it by this way :
if let c = contact as? GroupStatus {
if !c.isPrivate() {
nonPrivateContact.append(contact)
}
}
But I want to keep the static keyword
This looks like a bug or a non-supported feature. I would expect that
the following works:
if let gsType = contact.dynamicType as? GroupStatus.Type {
if gsType.isPrivate() {
// ...
}
}
However, it does not compile:
error: accessing members of protocol type value 'GroupStatus.Type' is unimplemented
It does compile with FamilyContact.Type instead of GroupStatus.Type. A similar problem is reported here:
Swift 1.1 and 1.2: accessing members of protocol type value XXX.Type' is unimplemented
Making isPrivate() an instance method instead of a class method is
the only workaround that I currently can think of, maybe someone comes
with a better solution ...
Update for Swift 2 / Xcode 7: As #Tankista noted below, this has
been fixed. The above code compiles and works as expected in Xcode 7 beta 3.
type(of: contact).isPrivate()
This should work in recent Swift.