API violation - multiple calls made to -[XCTestExpectation fulfill], How can i solve this problem

741 Views Asked by At

I'm writing unit test for my codes which using Combine framework

i have a async operation, so i use expectation(descrption:) to wait async operation

this is my code example

class MyViewModel {
  private var someOtherSubject: CurrentValueSubject<...>(...)
  var someOtherStream: ... { 
    return someOtherSubject.eraseToAnyPublisher()
  }
  
  init(...) { 
    .
    .
    bind()
  }

  private func bind() { 
    . 
    .
    .
    someOfMySubject
      .flatMap { someAsyncOperation }
      .sink { [weak self] ... in 
         self?.someOtherSubject.send(value2)
      }
      .store(..)
  }

  .
  .

  func handleSomeUserAction() {
    self.someOtherSubject.send(value1)
    self.someOfMySubject.send(someEvent)
  }
}

i'm trying to check value which emitted by someOtherStream when handleSomeUserAction is called

so i wrote test code like this

func test_handleSomeUserAction() { 
  // given 
  let expectation = expectation(description: "...")
  var result: ValueType?

  // when 
  sut.someOtherStream
    .dropFirst() // drop CurretValueSubject's default value
    .sink { 
      result = $0
      expectation.fulfill()
    }
    .store(...)
  sut.handleSomeUserAction()

  // then
  wait(for: [expectation], timeout: 1.0)
  let unwrapped = try XCTUnwrap(result)
  XCTAssertEqual(unwrapped, value1)
}

i know expectedFulfillmentCount is 1 because it's default value is 1

but test is failed and Xcode show me this message

API violation - multiple calls made to -[XCTestExpectation fulfill]

so i tried to debug, and i figured out expectation.fulfill() called twice despite of expectedFulfillmentCount is 1

and result is value2 not value1 that i expected

why this happend? i think async operation(flatMap {...}) is finished too fast and fulfill called twice.

i tried delay mock method but i think this is not the right solution

1

There are 1 best solutions below

0
dasdom On

You code is kind of strange. But it looks like you are sending two values to someOtherStream. So it is expected that the sink is called twice.