0
votes

I am writing tests for the method provide below. `

class ScrapedRecipeCache @Autowired constructor(private val cache: RecipeScrapingCacheService,
                                                private val recipeService: RecipeService) : ScrapedRecipeProvider {
    override fun provide(request: ScrapingRequest): Flux<ScrapedRecipe> =
            cache.retrieve(request.link)
                    .doOnNext { println(it) }
                    .flatMap { (link, _, recipeHash, error) ->
                        recipeService.findByHash(recipeHash)
                                .map { ScrapedRecipe(it, link, error)}
                                .switchIfEmpty(cache.remove(request.link).then(Mono.empty()))
                    }
                    .flux()


}

` The test looks as follows:

private val recipeFetched = Recipe("Tortellini", RecipeDifficulty.EASY, 15.0)

val cacheContents = RecipeScrapingResource("www.google.com", ScrapingOrigin.JAMIE_OLIVER, recipeFetched.hash,
                    mutableListOf(
                            pl.goolash.core.Exception("aa", ErrorType.WARNING, LocalDateTime.MIN)
                    ))
val request = ScrapingRequest("www.google.com", ScrapingOrigin.JAMIE_OLIVER, 4)

@BeforeEach
fun setUp() {
given(cache.retrieve("www.google.com")).willReturn(Mono.just(cacheContents))
given(recipeService.findByHash(recipeFetched.hash)).willReturn(Mono.just(recipeFetched))
}

@Test
@DisplayName("Then return data fetched from service and don't delete cache")
 fun test() {
      cacheFacade.provide(request)
                            .test()
                            .expectNext(ScrapedRecipe(recipeFetched, "www.google.com", cacheContents.error!!))
                            .expectComplete()
                            .verify()
      BDDMockito.verify(cache, BDDMockito.never()).remove(request.link)
                }

The test fails because cache.remove(request.link) is called. To my understanding (or from what I managed to gather from documentation) switchIfEmpty, should only be fired when recipeService.findByHash returns Mono.empty(). However the debugger shows that it returns mocked value of Mono.just(fetchedRecipe).

The interesting thing is that when I replace

.switchIfEmpty(cache.remove(request.link).then(Mono.empty()))

with

.switchIfEmpty(Mono.just(1).doOnNext{println("weeee")}.then(Mono.empty()))

Then weee is not printed hence it behaves as expected, that is switchIfEmpty is not fired.

Furthermore the tested issue runs properly in integration test and does not clear the cache.

Reactor version : 3.1.0-RC1 Other notable details: Spring Boot 2.0.0-M4, Mockito-core:2.10, junit 5, project is written in kotlin

The question is, does anybody see anything wrong with this? Because I have spent two days over and still have no clue why this behaves so bizzarely.

1

1 Answers

1
votes

Finally I found out how to make this work.

In order to remedy it:

 override fun provide(request: ScrapingRequest): Flux<ScrapedRecipe> =
        cache.retrieve(request.link)
                .flatMap { (link, _, recipeHash, error) ->
                    recipeService.findByHash(recipeHash)
                            .map { ScrapedRecipe(it, link, error) }
                            .switchIfEmpty(Mono.just(1)
                                    .flatMap { cache.remove(request.link) }
                                    .then(Mono.empty()))
                }
                .flux()

You can see how using flatMap to execute the asynch work does the job, even if this is not the neatest implementation, it revealed to me quite an interesting mechanism hidden here.