Swift: reflection thanks to infoDictionary - unit-testing

I try to instanciate a new class from a string class name, like we can easy do in Java. I have finally wrote this function:
func stringClassFromString(_ className: String) -> AnyClass! {
let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String;
let cls: AnyClass = NSClassFromString("\(namespace).\(className)")!;
return cls;
}
Thanks to some googling, but as soon as I tried this solution thanks to a unit test :
func test() {
let myclass = stringClassFromString("NSDate") as! NSDate.Type
let instance = myclass.init()
print(instance)
}
I have an exception (Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)) as soon as my test use the line : let namespace... I tried to see the content of the infoDictionary, he's empty. So my question is simple, is it because of the unit tests context that my dictionary is empty ? Is there any method or library (like robolectric for android) to simulate an infoDicionary for testing purpose.

I succeed to find my error, instead of using the main Bundle, I use the constructor of the Bundle class to build a Bundle which will correspond to the current context :
let namespace = Bundle(for: type(of: self)).infoDictionary!["CFBundleExecutable"] as! String;
And tadda, it's working !

Related

How to mock a package method in Go?

Let's say I have a package with the following code:
package paths
type FilePath struct {
PathA string
}
func (c FilePath) GetPathA() string {
if err := PathExists(PathA); err != nil {
return ""
}
return PathA + "foo"
}
func PathExists(p string) error {
// call os and file methods
return err
}
How do I mock out the PathExists dependency to test FilePath? Also, method PathExists is being used by a lot of other packages as well. (I am open to suggestions of refactoring this to make it test friendly, keeping the following pointers in mind)
I have come across a few different approaches but none of them seems intuitive or idiomatic to me.
Have a global variable PE := PathExists in the package; in GetPathA, call err := PE(PathA) and in the test overwrite PE with a mock method.
Issue: If test package is something like paths_test, I will have to export PE which allows clients of the package to overwrite it as well.
Make PathExists a field of FilePath and mock the field in test.
Issue: Clients when using the package, will have to initialize PathExists field, or I provide a constructor of the form NewFilePath(PathtA string) which initializes the fields for me. In the actual use case there are a lot of fields, hence this approach fails as well.
Use an interface and embed it within the struct. When client uses it initialize with the actual method and for test mock it.
type PathExistser interface{
PathExists(p string) error
}
type FilePath struct{
PathA string
PathExister
}
type Actual struct{}
func (a Actual) PathExists(p string) error {
return PathExists(p)
}
Issue: Client again needs to provide the right implementation of the interface.
I have learnt of few more approaches doing something simimlar to the above options, such as make the method PathExists an argument for GetPathA, etc. All have the same concerns. Basically, I don't want the users of this package to have to figure out what should be the right input parameter to make sure the struct works as expected. Neither do I want the users to overwrite the behaviour PathExists.
This seems like a very straightforward problem and I seem to be missing something very funamental about go testing or mocking. Any help would be appreciated, thanks.
Method names are just for example. In reality GetPathA or PathExists would be way more complex.
To address the issue from your 1. approach, you can use an internal package which you'll then be able to import in paths_test but clients of your package won't be.
package paths
import (
// ...
"<your_module_path>/internal/osutil"
)
func PathExists(p string) error {
return osutil.PathExists(p)
}
package osutil
var PathExists = func(p string) error {
// call os and file methods
return err
}
// Use a mutex to make sure that if you have
// multiple tests using mockPathExists and running
// in parallel you avoid the possiblity of a data race.
//
// NOTE that the mutex is only useful if *all* of your tests
// use MockPathExists. If only some do while others don't but
// still directly or indirectly cause the paths.PathExists
// function to be invoked then you still can run into a data
// race problem.
var mu sync.Mutex
func MockPathExists(mock func(p string) error) (unmock func()) {
mu.Lock()
original := PathExists
PathExists = mock
return func() {
PathExists = original
mu.Unlock()
}
}
package paths_test
import (
// ...
"<your_module_path>/internal/osutil"
)
func TestPathExists(t *testing.T) {
unmock := osutil.MockPathExists(myPathExistsMockImpl)
defer unmock()
// do your test
}

Swift. Error in working with UIApplication.shared

This question, in the old program that is implemented under UIKit, was done like this.
Condition in which the following actions are performed:
if(theApp().m_Disp.Connecttosrv(self.SelCon)) {
In the condition, the function is accessed
func theApp() -> iSPultApp!
{
return UIApplication.shared as? iSPultApp
}
Where next the class is called from the function
class iSPultApp: UIApplication {
var m_Disp: Chroot = Chroot()
Everything works there, is it possible to redo it somehow for SwiftUI?
The program goes to func theApp (), and then instead of going to the class, returns to the condition and there is an error:
if(theApp().m_Disp.Connecttosrv(self.SelCon)) {
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value.
self.SelCon is not empty, but filled with data. apparently, nil is passed in func theApp()
Thank you in advance for your help 🙏

Parse class objects no longer working in Xcode 8.3

I recently updated my Xcode to Xcode 8.3.2 and ran the swift syntax updater. As a result of this update my Parse classes no longer work and I am not sure how fix them.
The error I am receiving is 'Static member 'registerSubclass' cannot be used on instance of type 'County'
The class looks like this.
import Foundation
class County : PFObject, PFSubclassing {
private lazy var __once: () = {
self.registerSubclass()
}()
#NSManaged var Name: String
override class func initialize() {
var onceToken : Int = 0;
_ = self.__once
}
class func parseClassName() -> String{
return "County"
}
}
I am also not understanding why in this update I am getting errors on when using self to call class level methods from within an enclosure?

access methods from protocols to viewcontroller

I was just implementing object class to access methods and protocols. Following
import UIKit
import Foundation
import Alamofire
import SVProgressHUD
protocol ParseAPIProtocol:class {
func APIresponse(responseDict: NSDictionary)
}
class ParseAPI :NSObject{
// weak var mydelegate : MyProtocols?
weak var delegateAPI:ParseAPIProtocol?
class func ParseUrl(postparameters: [String: AnyObject]!, url:String, current:UIViewController) {
print(postparameters)
print(url)
SVProgressHUD.show()
Alamofire.request(url, method: .post, parameters: postparameters, encoding: URLEncoding.default, headers: nil).responseJSON
{
(response:DataResponse<Any>) in
switch(response.result)
{
case .success(_):
if response.result.value != nil
{
let jsonResult = (try! JSONSerialization.jsonObject(with: response.data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary
print(jsonResult)
}
break
case .failure(_):
SVProgressHUD.dismiss()
print(response.result.error as Any)
break
}
}
}
Whenever I got the jsonResult, I just want to call fun APIresponse to parse result dictionary to viewcontroller class.
I declared the method on viewcontroller and its delegate.
But when I tried to call protocol method, it doesn't give me access to the method. May be I am doing something wrong. Please look into my method.
Thanks!
The way I see it there are several issues, although it's hard to tell because you don't show the relevant code in your view controller
(how you set up the delegate, how you call your parse API).
First, you never call your delegate with the result. With your current setup this isn't even possible: you define ParseAPI to have a
delegate (instance) member but define ParseUrl as a class func. I guess this is what you meant when saying "it doesn't give me access to the method".
Try defining it as a member function and then, in the success case call the delegate:
self.delegateAPI?.APIresponse(responseDict: jsonResult)
In your view controller, instantiate a ParseAPI instance and keep it alive (for example by storing it in a strong instance member), set its delegate,
and call ParseUrl(...). This call will return pretty much immediately and later, when the request and response is handled, your delegate method will
be called.
class ViewController: UIViewController, ParseAPIProtocol
{
var parser: ParseAPI?
func somewhere()
{
self.parser = ParseAPI()
self.parser!.delegateAPI = self
self.parser!.ParseUrl(postparameters: [:], url: "...", current: self)
}
func APIresponse(responseDict: NSDictionary)
{
print("VC got response:", responseDict)
}
}
Second, the current parameter is never used, you can remove it. Either that or you can remove the whole delegate thing and replace the current parameter
with a callback, something like this:
func ParseUrl(postparameters: [String: AnyObject]!, url:String, finished: #escaping (_ response: NSDictionary?) -> Void)
{
...
// success case:
finished(jsonResult)
// failure case:
finished(nil)
}
and call it like this:
self.parser!.ParseUrl(postparameters: [:], url: "...", finished: { (response: NSDictionary?) in
// handle response
})
You can also define your callback to have an error parameter so you can act accordingly when one occurs.
And finally, while I don't know SVProgressHUD, you probably wanna call SVProgressHUD.dismiss() in the success case, too.

How to mock NSDate in Swift?

I have to test some date calculation but to do so I need to mock NSDate() in Swift. Whole app is written in Swift and I'd like to write test in it as well.
I've tried method swizzling but it doesn't work (or I'm doing something wrong which is more likely).
extension NSDate {
func dateStub() -> NSDate {
println("swizzzzzle")
return NSDate(timeIntervalSince1970: 1429886412) // 24/04/2015 14:40:12
}
}
test:
func testCase() {
let original = class_getInstanceMethod(NSDate.self.dynamicType, "init")
let swizzled = class_getInstanceMethod(NSDate.self.dynamicType, "dateStub")
method_exchangeImplementations(original, swizzled)
let date = NSDate()
// ...
}
but date is always current date.
Disclaimer -- I'm new to Swift testing so this may be a horribly hacky solution, but I've been struggling with this, too, so hopefully this will help someone out.
I found this explanation to be a huge help.
I had to create a buffer class between NSDate and my code:
class DateHandler {
func currentDate() -> NSDate! {
return NSDate()
}
}
then used the buffer class in any code that used NSDate(), providing the default DateHandler() as an optional argument.
class UsesADate {
func fiveSecsFromNow(dateHandler: DateHandler = DateHandler()) -> NSDate! {
return dateHandler.currentDate().dateByAddingTimeInterval(5)
}
}
Then in the test create a mock that inherits from the original DateHandler(), and "inject" that into the code to be tested:
class programModelTests: XCTestCase {
override func setUp() {
super.setUp()
class MockDateHandler:DateHandler {
var mockedDate:NSDate! = // whatever date you want to mock
override func currentDate() -> NSDate! {
return mockedDate
}
}
}
override func tearDown() {
super.tearDown()
}
func testAddFiveSeconds() {
let mockDateHandler = MockDateHandler()
let newUsesADate = UsesADate()
let resultToTest = usesADate.fiveSecondsFromNow(dateHandler: mockDateHandler)
XCTAssertEqual(resultToTest, etc...)
}
}
If you want to swizzle it you need to swizzle a class that is internally used by NSDate and it is __NSPlaceholderDate. Use this only for testing since it is a private API.
func timeTravel(to date: NSDate, block: () -> Void) {
let customDateBlock: #convention(block) (AnyObject) -> NSDate = { _ in date }
let implementation = imp_implementationWithBlock(unsafeBitCast(customDateBlock, AnyObject.self))
let method = class_getInstanceMethod(NSClassFromString("__NSPlaceholderDate"), #selector(NSObject.init))
let oldImplementation = method_getImplementation(method)
method_setImplementation(method, implementation)
block()
method_setImplementation(method, oldImplementation)
}
And later you can use like this:
let date = NSDate(timeIntervalSince1970: 946684800) // 2000-01-01
timeTravel(to: date) {
print(NSDate()) // 2000-01-01
}
As others suggested I would rather recommend introducing a class Clock or similar that you can pass around and get a date from it and you can easily replace it with an alternative implementation in your tests.
Rather than use swizzling you should really design your system to support testing. If you do a lot of data processing then you should inject the appropriate date into the functions which use it. In this way your test injects the dates into these functions to test them and you have other tests which verify that the correct dates will be injected (when you stub the methods that use the dates) for various other situations.
Specifically for your swizzling problem, IIRC NSDate is a class cluster so the method you're replacing is unlikely to be called as a different class will be 'silently' created and returned.