Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Test function that uses PromiseKit

I have the following function which I want to test:

func getProduct(ean: String) -> Promise<Product> {
    return Promise { fullfill, reject in
        let urlString = BSConstants.BarcodeScanner.productEndpoint.stringByAppendingString(ean)
        serviceClient.GET(urlString, failure: { (error) in
            reject(error!)
        }) { (response) in
            if let json = response {
                self.storeProduct(json).then ({ returnedProduct in
                    fullfill(returnedProduct)
                }).error { returnedError in
                    // HANDLE ERROR
                    print("HANDLE ERROR")
                }
            }
        }
    }
}

Here is the mock manager I created.

class MockStoreProductProductManager: BSProductManagerSwift {
    var storeProductWasCalled = false
    var storeProductJSON: JSON?
    let deferredPromise = Promise<Product>.pendingPromise()

    override func storeProduct(json: JSON) -> Promise<Product> {
        storeProductWasCalled = true
        storeProductJSON = json

        return deferredPromise.promise
    }
}

And here is the non-working test.

func testGetProduct_WithInStockEAN_RetunsValidInstockJSON() {
    // Given
    let mockManager = MockStoreProductProductManager(serviceClient: BSNetworkingServiceClient())
    let inputEanString = "1234567891234"

    var expectedJSON: JSON? = nil
    if let jsonPath = NSBundle(forClass: self.dynamicType).pathForResource("getproduct-instock-response", ofType: "json") {
        let data = NSData(contentsOfFile: jsonPath)!
        stub(http(.GET, uri: "some-url-i-have-removed"), builder: jsonData(data))

        let jsonResponse = JSON(data: data)
        expectedJSON = jsonResponse["data"]
    }

    // When

    let exp = expectationWithDescription("Return instock product JSON")

    mockManager.getProduct(inputEanString).then { returnedProduct in
        exp.fulfill()
    }.error { error in
        XCTFail("🔴 Instock JSON should have been returned")
        exp.fulfill()
    }

    waitForExpectationsWithTimeout(10, handler: nil)

    // Then
    XCTAssertTrue(mockManager.storeProductWasCalled, "🔴 Expected storeProduct function to be called but it wasn't")
    XCTAssertEqual(mockManager.storeProductJSON, expectedJSON, "🔴 Incorrect JSON passed to storeProduct")
}

I know the mock manager is getting called but my expectation never gets fulfilled (exp.fulfill() is not called) and I don't know why. Any ideas?

like image 579
Hodson Avatar asked Aug 10 '16 13:08

Hodson


1 Answers

Turns out I was almost there but I had to make my mock manager return a fulfill and pass a Product (NSManagedObject subclass). So now my mock manager looks like this and my tests work:

class MockStoreProductProductManager: BSProductManagerSwift {
    var storeProductWasCalled = false
    var storeProductJSON: JSON?
    let deferredPromise = Promise<Product>.pendingPromise()

    override func storeProduct(json: JSON) -> Promise<Product> {
        storeProductWasCalled = true
        storeProductJSON = json

        let managedObjectContext = setUpInMemoryManagedObjectContext()
        let entity = NSEntityDescription.entityForName("Product", inManagedObjectContext: managedObjectContext)
        let product = Product(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)
        deferredPromise.fulfill(product)

        return deferredPromise.promise
    }
}
like image 88
Hodson Avatar answered Oct 10 '22 17:10

Hodson