swift 在iOS测试中以编程方式模拟GPS定位

u3r8eeie  于 2022-12-10  发布在  Swift
关注(0)|答案(4)|浏览(265)

我想在Swift中编写UI测试,在我们的应用程序中对Map的不同位置进行截图。为了做到这一点,我需要在测试期间模拟假GPS数据。
有一些解决方案像这样的一个(https://blackpixel.com/writing/2016/05/simulating-locations-with-xcode-revisited.html),使用GPX文件,并模拟Xcode中的Debug > Simulate Location的位置,但我需要这是完全自动化的。理想的是类似于LocationManager在Android中。

eulz3vhy

eulz3vhy1#

我在编写UI测试时也遇到过类似的问题,因为模拟器/绑定设备不能做你想做的所有事情。我所做的是编写模仿所需行为的模拟(我通常无法控制的行为)。
用自定义位置管理器替换CLLocationManager将允许您完全控制位置更新,因为您可以通过CLLocationManagerDelegate方法以编程方式发送位置更新:locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) .
创建一个类MyLocationManager,使其成为CLLocationManager的子类,并让它覆盖所有调用的方法。不要在被覆盖的方法中调用super,因为CLLocationManager实际上不应该接收方法调用。

class MyLocationManager: CLLocationManager {
  override func requestWhenInUseAuthorization() {
    // Do nothing.
  }

  override func startUpdatingLocation() {
    // Begin location updates. You can use a timer to regularly send the didUpdateLocations method to the delegate, cycling through an array of type CLLocation.
  }

  // Override all other methods used.

}

delegate属性不需要被覆盖(也不能被覆盖),但您可以将其作为CLLocationManager的子类进行访问。
若要使用MyLocationManager,您应该传入启动参数,以告知您的应用是否为UITest。在测试用例的setUp方法中插入以下代码行:

app.launchArguments.append("is_ui_testing")

将CLLocationManager存储为在测试时为MyLocationManager的属性。在不测试时,将正常使用CLLocationManager。

static var locationManger: CLLocationManager = ProcessInfo.processInfo.arguments.contains("is_ui_testing") ? MyLocationManager() : CLLocationManager()
hsvhsicv

hsvhsicv2#

为了进行(单元)测试,您可以混合startUpdatingLocation方法,以指示CLLocationManager示例按照您希望的方式运行。
下面是一个可能的实现:

// Just a wrapper to allow storing weak references in an array
struct WeakRef<T: AnyObject> {
    weak var value: T?
}

extension CLLocationManager {
    
    // Replace the `startUpdatingLocation` method with our own
    private static var swizzled = false
    static func swizzle() {
        guard !swizzled else { return }
        swizzled = true
        method_exchangeImplementations(class_getInstanceMethod(self, #selector(startUpdatingLocation))!,
                                       class_getInstanceMethod(self, #selector(swizzledStartUpdatingLocation))!)
    }
    
    // When a CLLocationManager is asked to `startUpdatingLocation`,
    // it adds itself to the list of registered instances
    // Using weak references to allow managers to deallocate when needed
    static var registeredInstances = [WeakRef<CLLocationManager>]()
    @objc func swizzledStartUpdatingLocation() {
        Self.registeredInstances.append(WeakRef(value: self))
        if let mockedLocation = Self.mockedLocation {
            delegate?.locationManager?(self, didUpdateLocations: [mockedLocation])
        }
    }
    
    // This is the mocked location, when it changes
    // all registered managers notify their delegates with the new mocked location
    static var mockedLocation: CLLocation? {
        didSet {
            guard let mockedLocation = mockedLocation else { return }
            registeredInstances.forEach {
                guard let manager = $0.value else { return }
                manager.delegate?.locationManager?(manager, didUpdateLocations: [mockedLocation])
            }
        }
    }
}

当然,这只是stopUpdatingLocation的一个基本实现,还应该swizzled删除管理器。CLLocationMangager的其他特性也可以在扩展中实现/swizzled。

tsm1rwdh

tsm1rwdh3#

不能。CLLocationManager正在代理的帮助下提供您的位置,您可以通过任何方式设置此位置。
您可以创建一个CLLocationManager模拟器类,它可以给予一些随时间变化的位置。或者您可以将您的测试与一个带有时间戳的GPX同步。

ffdz8vbo

ffdz8vbo4#

另一个选择是将XCTest绑定到一个本地Web服务器,它将模拟位置,例如,通过idb。要了解更多细节,请随时查看这个blog post。希望它能有所帮助。

相关问题