Considere este teste de unidade baseado no XCTest:
func testImageRetrieved() {
let expectation = XCTestExpectation()
let cancellable = viewModel.$image.dropFirst().sink { _ in
// Do nothing on completion.
} receiveValue: {
XCTAssertNotNil($0)
expectation.fulfill()
}
wait(for: [expectation], timeout: 1.0)
cancellable.cancel()
}
De acordo com o Migrating a test from XCTest da Apple , isso deve ser diretamente traduzível neste método baseado em testes Swift:
@Test
func imageRetrieved() async {
var cancellable: AnyCancellable?
await confirmation { imageRetrieved in
cancellable = viewModel.$image.dropFirst().sink { _ in
// Do nothing on completion.
} receiveValue: {
#expect($0 != nil)
imageRetrieved()
}
}
cancellable?.cancel()
}
O último teste, no entanto, falha com o erro dizendo: "A confirmação foi confirmada 0 vezes, mas esperava-se que fosse confirmada 1 vez". Parece que a confirmação não "espera" até que o publicador emita um valor.
Qual é a maneira correta de testar editores do Combine no Swift Testing?
Correto. A documentação diz,
O closure neste caso retorna quase imediatamente, já que tudo o que ele faz é atribuir a uma variável. O teste não verá nenhum valor que seja publicado de forma assíncrona.
Compare isso com o exemplo no guia de migração.
No final do fechamento,
await Customer().buy(.soup)
é chamado, e é isso que presumivelmente irá dispararFoodTruck.shared.eventHandler
.Para publicadores, você pode facilmente obter its
values
como umAsyncSequence
, o que é muito mais fácil de consumir. Por exemplo, para verificar se o publicador publica pelo menos um elemento, e se o primeiro elemento não é nil. você pode fazer:Considere também adicionar um
timeout
antes.values
.Alternativamente, você pode esperar manualmente apenas chamando
Task.sleep
. Os testes a seguirpublisher
publicam exatamente um elemento, que não é nulo, em um período de 2 segundos.Claro, isso faz com que o teste seja executado por pelo menos 2 segundos, o que pode ser indesejável às vezes.
Você está correto, que “
confirmation
não ‘espera’ até que o publicador emita um valor”. Como diz o documento :Se você observar os
confirmation
exemplos nesse documento, todos eles fazemawait
algumaasync
chamada antes de retornar.Mas você pode usar
values
para renderizar seuPublisher
como umAsyncSequence
, o que você pode simplesmenteawait
em umasync
teste. Então, em vez desink
, apenas pegue o primeiro valor desta sequência assíncrona:Então, no seu caso, você deve ser capaz de fazer:
Com esta extensão para simplificar a chamada para
first(where:)
: