Skip to content

Instantly share code, notes, and snippets.

@anishparajuli555
Created May 31, 2017 07:22
Show Gist options
  • Save anishparajuli555/d9f3f51e4af1102ec77cad09b916b1a7 to your computer and use it in GitHub Desktop.
Save anishparajuli555/d9f3f51e4af1102ec77cad09b916b1a7 to your computer and use it in GitHub Desktop.
//I have a location manager class. This class just holds user current location so that it can be used later in the project.
//This will be singleton class later.Since singletons are hard to test(Because of no DI injection mechanism).I have not
implemented yet for now.The problem is at the end.
class HelperLocationManager:NSObject{
let locationManager:CLLocationManager
init(mgr:CLLocationManager) {
self.locationManager = mgr
super.init()
self.locationManager.delegate = self
}
}
extension HelperLocationManager:CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("authorization calling in real helper class")
switch status {
case CLAuthorizationStatus.notDetermined:
locationManager.requestWhenInUseAuthorization()
case CLAuthorizationStatus.restricted:
print("Restricted Access to location")
case CLAuthorizationStatus.denied:
print("User denied access to location")
case CLAuthorizationStatus.authorizedWhenInUse:
self.locationManager.startUpdatingLocation()
default:
print("default authorization")
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locations calling in real helper class")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error ----", error)
}
}
//Some of my test cases to test this class is
class LocationTestTests: XCTestCase {
var locationHelper:HelperLocationManager!
var locationMgr:CLLocationManager!
override func setUp() {
super.setUp()
locationMgr = CLLocationManager()
locationHelper = HelperLocationManager(mgr: locationMgr)
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testLocationManagerIsSet(){
XCTAssertNotNil(locationHelper.locationManager)
}
func testLocationManagerDelegateIsSet(){
XCTAssertNotNil(locationHelper.locationManager.delegate)
XCTAssert((locationHelper.locationManager.delegate!.isKind(of: HelperLocationManager.self)))
}
func testDidUpdateLocationCallsOnAuthorizationStatusAllowed(){
class FakeLocationManager:CLLocationManager{
}
let fakeLocationManager = FakeLocationManager()
locationHelper = HelperLocationManager(mgr: fakeLocationManager)
locationHelper.locationManager.delegate!.locationManager!(fakeLocationManager, didChangeAuthorization: .authorizedWhenInUse)
}
func testPopUpMessageShownWhenStatusDeniend(){
class FakeLocationManager:CLLocationManager{
}
let fakeLocationManager = FakeLocationManager()
locationHelper = HelperLocationManager(mgr: fakeLocationManager)
locationHelper.locationManager.delegate!.locationManager!(fakeLocationManager, didChangeAuthorization: .denied)
}
func testWhwnLocationIsSetToNepal(){
class FakeLocationManager:CLLocationManager{
}
class FakeLocationManagerDelegate:NSObject, CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let loc = locations.first!
print("mock location is \(loc.coordinate.latitude), \(loc.coordinate.longitude)")
}
}
let fakeDelegate = FakeLocationManagerDelegate()
let fakeLocationManager = FakeLocationManager()
locationHelper = HelperLocationManager(mgr: fakeLocationManager)
locationHelper.locationManager.delegate = fakeDelegate
((locationHelper.locationManager.delegate) as! FakeLocationManagerDelegate).locationManager(locationHelper.locationManager, didUpdateLocations: [CLLocation(latitude:21.2,longitude:23.22)])
}
}
//My Question are as follows
1) What approach should i use to test this HelperLocationManager class? Mocking, swizzling, or direct parameters injection?
2) How can i test the behaviour, locationManager didUpdateLocations method is called only if CLAuthorizationStatus is
authorizedWhenInUse ?
3) What are the possible test cases for it?
@jonreid
Copy link

jonreid commented Jun 2, 2017

If I understand correctly, you want to write unit tests for HelperLocationManager. The way to test the CLLocationManagerDelegate methods is to have tests call them directly, as if they had been called by Apple's frameworks.

Then all that's left is for HelperLocationManager not to use a direct reference to a CLLocationManager. Instead, I'd make that a protocol, so that tests can inject a mock instead. To see how I make mocks, check out http://qualitycoding.org/swift-mock-objects/

Then I'd have separate tests for each delegate method, and each path through them. For example, I'd have a test call didChangeAuthorization with .authorizedWhenInUse . Separate tests would pass other status values.

…Does that help?

@anishparajuli555
Copy link
Author

Yes this helps but i want to mock the behavior didUpdateLocations should only be called on authorizedWhenInUse status. Is it possible?

  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        print("locations calling in real helper class")
        
    }
    

@jonreid
Copy link

jonreid commented Jun 16, 2017

But what is responsible for calling didUpdateLocations on your delegate? Isn't it Apple's Core Location, calling you? There is no need to test Apple's code.

What you can test instead is that you've set up Core Location correctly. That is, you can test that you are passing the desired CLAuthorizationStatus to Core Location.

But everything beyond that is not your concern. You can trust that, when set correctly, Core Location will call you back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment