我有一个带有静态方法的以下类,该方法使用MKDirections计算两个坐标之间的自定义路线。一旦完成计算,该方法将使用委托将路线(MKPolyline对象)传递给视图控制器,该视图控制器将其添加为叠加层的MapView。每条路线都分配有一个标题,该标题确定该路线在地图上呈现的颜色。
class NavigationInterface {
weak static var routeDelegate: RouteDelegate!
static func addRouteFromTo(sourceCoor: CLLocationCoordinate2D, destinationCoor: CLLocationCoordinate2D, transportTypeString: String)
{
let sourcePlacemark = MKPlacemark(coordinate: sourceCoor)
let destinationPlacemark = MKPlacemark(coordinate: destinationCoor)
//var route = MKRoute()
let request = MKDirectionsRequest()
request.source = MKMapItem(placemark: sourcePlacemark)
request.destination = MKMapItem(placemark: destinationPlacemark)
request.requestsAlternateRoutes = false
//get MKDirectionsTransportType based on String identifier
request.transportType = getTransportType(transportTypeString: transportTypeString)
let directions = MKDirections(request: request)
directions.calculate { (response, error) in
if let directionResponse = response?.routes.first {
let route = directionResponse.polyline
route.title = transportTypeString
print("Got Here")
self.routeDelegate!.didAddRoute(route: route)
}
}
}
委托是通过以下协议定义的:
protocol RouteDelegate: class {
func didAddRoute(route: MKPolyline)
func didAddBoundary(boundary: MKPolygon)
}
View Controller如下实现委托:
class MapViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
...
override func viewDidLoad() {
super.viewDidLoad()
NavigationInterface.routeDelegate = self
}
extension MapViewController: RouteDelegate {
// delegate Method
// called in Navigation Interface
func didAddRoute(route: MKPolyline) {
mapView.add(route)
}
func didAddBoundary(boundary: MKPolygon) {
mapView.add(boundary)
}
}
现在我试图编写一个UnitTest来检查委托方法“ didAddRoute”是否返回正确的路由
为此,我创建了一个测试类“ NavigationTests”,该类实现RouteDelegate协议,以及一个测试方法,该方法计算路由,然后评估从“ didAddRoute”的“ NavigationTests”协议实现返回的路由:
class NavigationTests: XCTestCase, RouteDelegate {
var routes = [MKPolyline]()
var asyncExpectation: XCTestExpectation?
func didAddRoute(route: MKPolyline) {
routes.append(route)
asyncExpectation?.fulfill()
}
...
func testaddRouteFromTo(){
NavigationInterface.routeDelegate = self
asyncExpectation = expectation(description: "routes returned from delegate method")
NavigationInterface.addRouteFromTo(sourceCoor: CoordinateA, destinationCoor: CoordinateB, transportTypeString: "roadTravel")
let result = XCTWaiter.wait(for: [self.asyncExpectation!], timeout: 2.0)
if result == XCTWaiter.Result.completed {
let route = self.routes.first
XCTAssert(route!.title == "roadTravel", "failed to retrieve correct route")
print(route!.title)
} else {
XCTFail()
}
}
}
现在,此测试方法从RouteDelegate的MapViewController实现而不是NavigationTests实现随机返回路线。 W
如何避免对MapViewController的这些不必要的引用,以及为什么由于我没有在测试中实例化它而完全创建它? 理想情况下,我希望防止在运行此测试类时实例化MapViewController,因为单元测试不需要它。
如何确保仅使用RouteDelegate的NavigationTests实现?
答案 0 :(得分:2)
由于addRouteFromTo(sourceCoor:destinationCoor:transportTypeString:)
是静态方法,因此您也将NavigationInterface.routeDelegate
设为静态。测试运行时,它们正在设置全局变量。这意味着测试的副作用会持续超出测试范围。
有两种方法可以防止这种情况的发生:
a)创建一个setUp()
和tearDown()
。在setUp()
中,保存NavigationInterface.routeDelegate
的旧值,然后将其覆盖到self
中。在tearDown()
中,恢复旧值。
b)从静态更改为对象。通常,静态会使事情更难测试。
首选b)。它更安全,可测试性的压力可以改善您的设计。
...我在测试中没有看到对MapViewController的任何引用。它是由您的应用程序代表创建的吗?
现在,您的问题更大了。进行实际联网的测试缓慢且脆弱。这取决于您的网络状况。这取决于后端。它引入了时间差。
重组代码可以更好地为您服务,以便您可以测试以下内容:
这将在至少两次测试中表达,但可能还会更多。一旦可以独立测试响应处理,就可以测试错误以及成功的响应。
那么您如何独立于“处理响应”来测试“创建响应”?通过用单独的方法完成这项工作。然后测试可以调用这些方法。
无需测试Apple进行网络通话,在后端执行某些操作或发送响应。如果您采用这种方法,那么异步测试的需求就会大大降低。
我希望这会有所帮助。如果需要澄清,请询问。有关“ Apple向我们展示编写代码的方式不是可测试的设计的方式”的更多想法,请参见https://qualitycoding.org/design-sense/