单元测试iOS 10通知 [英] Unit testing iOS 10 notifications

查看:125
本文介绍了单元测试iOS 10通知的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用中,我想断言通知已以正确的格式添加.我通常会使用依赖项注入来做到这一点,但我想不出一种方法来测试新的UNUserNotificationCenter API.

In my app I wish to assert that notifications have been added in the correct format. I'd normally do this with dependency injection, but I can't think of a way to test the new UNUserNotificationCenter API.

我开始创建一个模拟对象,该对象将捕获通知请求:

I started to create a mock object which would capture the notification request:

import Foundation
import UserNotifications

class NotificationCenterMock: UNUserNotificationCenter {
    var request: UNNotificationRequest? = nil
    override func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)? = nil) {
        self.request = request
    }
}

但是,UNUserNotificationCenter没有可访问的初始化程序,我无法实例化该模拟程序.

However, UNUserNotificationCenter has no accessible initializers I can't instantiate the mock.

我什至不确定我是否可以通过添加通知请求并获取当前通知来进行测试,因为测试将需要请求对模拟器的许可,这将使测试停止.目前,我已经将通知逻辑重构为包装器,因此至少可以在整个应用程序中对其进行模拟并手动进行测试.

I'm not even sure I can test by adding the notification request and fetching the current notifications, as the tests would need to request permission on the Simulator which would stall the tests. Currently I've refactored the notification logic into a wrapper, so I can at least mock that throughout my application and manually test.

我有比手动测试更好的选择吗?

Do I have any better options than manual testing?

推荐答案

您可以为正在使用的方法创建协议,并在UNUserNotificationCenter上进行扩展以使其符合要求. 该协议将充当原始UNUserNotificationCenter实现与您的模拟对象之间的桥梁",以替换其方法实现.

You can create a protocol for the methods you are using, and make an extension on UNUserNotificationCenter to conform to it. This protocol would act as a "bridge" between the original UNUserNotificationCenter implementation and your mock object to replace its method implementations.

这是我在操场上编写的示例代码,并且运行良好:

Here's an example code I wrote in a playground, and works fine:

/* UNUserNotificationCenterProtocol.swift */

// This protocol allows you to use UNUserNotificationCenter, and replace the implementation of its 
// methods in you test classes.
protocol UNUserNotificationCenterProtocol: class {
  // Declare only the methods that you'll be using.
  func add(_ request: UNNotificationRequest,
           withCompletionHandler completionHandler: ((Error?) -> Void)?)
}

// The mock class that you'll be using for your test classes. Replace the method contents with your mock
// objects.
class MockNotificationCenter: UNUserNotificationCenterProtocol {

  var addRequestExpectation: XCTestExpectation?

  func add(_ request: UNNotificationRequest,
           withCompletionHandler completionHandler: ((Error?) -> Void)?) {
    // Do anything you want here for your tests, fulfill the expectation to pass the test.
    addRequestExpectation?.fulfill()
    print("Mock center log")
    completionHandler?(nil)
  }
}

// Must extend UNUserNotificationCenter to conform to this protocol in order to use it in your class.
extension UNUserNotificationCenter: UNUserNotificationCenterProtocol {
// I'm only adding this implementation to show a log message in this example. In order to use the original implementation, don't add it here.
  func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)?) {
    print("Notification center log")
    completionHandler?(nil)
  }
}

/* ExampleClass.swift */

class ExampleClass {

  // Even though the type is UNUserNotificationCenterProtocol, it will take UNUserNotificationCenter type
  // because of the extension above.
  var notificationCenter: UNUserNotificationCenterProtocol = UNUserNotificationCenter.current()

  func doSomething() {
    // Create a request.
    let content = UNNotificationContent()
    let request = UNNotificationRequest(identifier: "Request",
                                           content: content,
                                           trigger: nil)
    notificationCenter.add(request) { (error: Error?) in
      // completion handler code
    }
  }
}

let exampleClass = ExampleClass()
exampleClass.doSomething() // This should log "Notification center log"

EDITED:
/* TestClass.Swift (unit test class) */

class TestClass {
  // Class being tested 
  var exampleClass: ExampleClass!    
  // Create your mock class.
  var mockNotificationCenter = MockNotificationCenter()

  func setUp() {
     super.setUp()
     exampleClass = ExampleClass()
     exampleClass.notificationCenter = mockNotificationCenter 
  }

  func testDoSomething() {
    mockNotificationCenter.addRequestExpectation = expectation(description: "Add request should've been called")
    exampleClass.doSomething()
    waitForExpectations(timeout: 1)
  }
}
// Once you run the test, the expectation will be called and "Mock Center Log" will be printed

请记住,每次使用新方法时,都必须将其添加到协议中,否则编译器会抱怨.

Keep in mind that every time you use a new method, you'll have to add it to the protocol, or the compiler will complain.

希望这会有所帮助!

这篇关于单元测试iOS 10通知的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆