Unit testing iOS 10 notifications

前端 未结 3 492
太阳男子
太阳男子 2021-02-05 13:13

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

3条回答
  •  遥遥无期
    2021-02-05 13:32

    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.

    Hope this helps!

提交回复
热议问题