I am trying to skip Single.delay() and my test falls with error:
java.lang.AssertionError: Not completed (latch = 1, values = 0, errors = 0, completions = 0)
val testScheduler = TestScheduler()
RxJavaPlugins.setComputationSchedulerHandler { testScheduler }
Single.just("")
.delay(1, TimeUnit.SECONDS, testScheduler)
.test()
.assertComplete()
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
this code works ok with Observable, is there any way to skip .delay() for Single?
You are asserting immediately before you even moved the time. Assert separately:
val testScheduler = TestScheduler()
val to = Single.just("")
.delay(1, TimeUnit.SECONDS, testScheduler)
.test()
to.assertEmpty();
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
to.assertComplete();
Also setting the default scheduler and using a custom scheduler with the operator makes no sense.
Related
I am trying to validate that a suspend function does not return anything at all in a certain test.
Consider the following situation:
val completionSignal = Channel<Unit>(capacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST)
suspend fun waitForCompletionSignal(): String {
completionSignal.receive()
return "Completion signal received"
}
I want to test this code with 2 unit tests, one that validates it returns the string when I provide the CompletionSignal with a value (thats the easy one).
And one that validates that it does not return anything when i don't give it anything. This is the hard one, since how long should I wait? And can i be sure the test fails if my code changes and suddenly the string is returned?
I got the following approach but I am missing some pieces:
#Test
fun `waitForCompletionSignal when completionSignal is provided assert result`() = runTest {
// Start waiting for result
val result = async { waitForCompletionSignal() }
// Provide completion signal
completionSignal.trySend(Unit)
// Await result and verify its correct
assertThat(result.await() == "Completion signal received")
}
#Test
fun `waitForCompletionSignal when completionSignal is not provided assert no result`() = runTest {
// Start waiting for result
val result = async { waitForCompletionSignal() }
// TODO?? some validation that succeeds if the test is just like this, but fails when i do the following:
completionSignal.trySend(Unit)
// A regular await would wait indefinately, and checking if the deferred result is completed does not work very well as well.
}
I hope the question is clear, thanks in advance.
I made an extension function on the deferred type to be able to wait for a max amount of time and after that it will return null. In my particular situation a delay time of 0 (so no delay whatsoever) is enough, but I can imagine that in some situations its useful to delay for a minimum amount of time.
#ExperimentalCoroutinesApi
suspend inline fun <reified T> Deferred<T>.awaitOrNull(
time: Long = 0,
crossinline actBlock: () -> Unit = { }
): T? = coroutineScope {
actBlock()
val timeoutJob = async<T?> {
delay(time)
null
}
return#coroutineScope select<T?> {
this#awaitOrNull.onAwait {
timeoutJob.cancel()
it
}
timeoutJob.onAwait {
this#awaitOrNull.cancel()
it
}
}
}
Using this method i can write the following tests that fail / succeed as expected:
// Succeeds
#Test
fun `waitForCompletionSignal when completionSignal is provided assert result`() = runTest {
val result = async {
waitForCompletionSignal()
}.awaitOrNull {
completionSignal.trySend(Unit)
}
assertThat(result == "Completion signal received")
}
// Succeeds
#Test
fun `waitForCompletionSignal when completionSignal is not provided assert no result`() = runTest {
val result = async {
waitForCompletionSignal()
}.awaitOrNull()
assertThat(result == null)
}
// Fails (to prove that it works)
#Test
fun `waitForCompletionSignal when completionSignal is not provided assert no result`() = runTest {
val result = async {
waitForCompletionSignal()
}.awaitOrNull {
completionSignal.trySend(Unit) // This (obviously) causes the test to fail, as I wanted.
}
assertThat(result == null)
}
I'm new in Unit Testing, I can't understand how to test this kind of method with kotlin, using MockK:
override fun register(firebaseId: String, uniqueId: String): Completable {
return Observable.just(0).observeOn(schedulerProvider.io()).flatMap {
val requestRegisterPushes = registerBuilder.build(firebaseId, uniqueId)
apiServiceFactory.build()
.registerPushes(requestRegisterPushes)
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
}.flatMapCompletable {
Completable.complete()
}
}
This is my code for the test, the test was a success but the condition coverage does not increase.
#Test
fun `register_Test()`() {
val requestRegisterPushes = mockk<RequestRegisterPushes>(relaxed = true)
every { registerBuilder.build(any(), any(), any(), any()) } returns requestRegisterPushes
every { apiServiceFactory.build().register(requestRegisterPushes) } returns Observable.just(SimpleResponse())
val resp = userRepository.register("x7gbyb68837g78s", "XXX-XXX-XXX")
}
I would really appreciate it if you could help me a little.
Of course you don't have any coverage because code inside flatmap and flatMapCompletable operator was not executed.
You need to subscribe to Observable to make it emmit elements in you case it will emmit 0 only when you subscribe for it. That's how RxJava works. Something like this:
val subscriber = TestSubscriber<>();
val resp = userRepository.register("x7gbyb68837g78s", "XXX-XXX-XXX").subscribe(subscriber)
subscriber.assertComplete()
I have a test here, where I am attempting to validate what is being sent to a ConflatedBroadcastChannel (experimental API, I know):
#Test
fun myTest() = runBlockingTest {
val results = ArrayList<String>()
val myChannel = ConflatedBroadcastChannel<String>()
myChannel.openSubscription().consumeEach {
results.add(it)
}
myChannel.send("hello")
assertEquals(1, results.size)
}
But when I run that code, I receive an IllegalStateException with the messge: "This job has not completed yet". Anyone know what's missing? I've tried using TestCoroutineDispatcher as well to no avail.
If you just want to obtain a single value from a channel you can always use receive.
Since ConflatedBroadcastChannel caches the latest value, you can just use it like this:
#Test
fun myTest() = runBlockingTest {
val myChannel = ConflatedBroadcastChannel<String>()
myChannel.send("hello")
val subscription = myChannel.openSubscription()
val result = subscription.receive()
subscription.cancel()
assertEquals("hello", result)
}
So I thought about the message I was getting, and suspected maybe that the "job that's not completed yet" was the .consumeEach operation I was performing. So to be explicit, I wrapped the operation in a launch(myTestCoroutineDispatcher) and called cancel. This doesn't seem like the ideal way to test things, but it makes sense given that the test environment isn't really guessing when your channel should stop receiving things. Anyways, lo and behold, this works:
#Test
fun myTest() = runBlockingTest {
val results = ArrayList<String>()
val myChannel = ConflatedBroadcastChannel<String>()
launch(testMainDispatcher) {
myChannel.openSubscription().consumeEach {
results.add(it)
cancel()
}
}
myChannel.send("hello")
assertEquals(1, results.size)
}
EDIT: another thing you can do to achieve same results is val myJob = launch {...} and then at the end of your test execution, call myJob.cancel() for the same effect.
I'm trying to listen to sqs using akka streams and i get messages from it's q
using this code snippet:
of course this code snippet get messages one-by-one (then ack it):
implicit val system = ActorSystem()
implicit val mat = ActorMaterializer()
implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(ioThreadPoolSize))
val awsSqsClient: AmazonSQSAsync = AmazonSQSAsyncClientBuilder
.standard()
.withCredentials(new ClasspathPropertiesFileCredentialsProvider())
.withEndpointConfiguration(new EndpointConfiguration(sqsEndpoint, configuration.regionName))
.build()
val future = SqsSource(sqsEndpoint)(awsSqsClient)
.takeWhile(_ => true)
.mapAsync(parallelism = 2)(m => {
val msgBody = SqsMessage.deserializeJson(m.getBody)
msgBody match {
case Right(body) => val id = getId(body) //do some stuff with the message may save state according the id
}
Future(m, Ack())
})
.to(SqsAckSink(sqsEndpoint)(awsSqsClient))
.run()
my question is:
can i get several messages, and save them for example in a stateful map for latter use?
for example that after receiving 5 messages (all of them will saved (per state))
then if specific condition happens i will ack them all, and if not they will return into queue (will happen anyway because visibility timeout)?
thanks.
Could be that you're looking for grouped (or groupedWithin) combinator. These allow you to batch messages and process them in groups. groupedWithin allows you to release a batch after a certain time in case it hasn't yet reached your determined size. Docs reference here.
In a subsequent check flow you can perform any logic you need, and emit the sequence in case you want the messages to be acked, or not emit them otherwise.
Example:
val yourCheck: Flow[Seq[MessageActionPair], Seq[MessageActionPair], NotUsed] = ???
val future = SqsSource(sqsEndpoint)(awsSqsClient)
.takeWhile(_ => true)
.mapAsync(parallelism = 2){ ... }
.grouped(5)
.via(yourCheck)
.mapConcat(identity)
.to(SqsAckSink(sqsEndpoint)(awsSqsClient))
.run()
In Mockito there is option to verify if mock method has been called, and specify timeout for this verification (VerificationWithTimeout), for example:
verify(mock, timeout(200).atLeastOnce()).baz();
It there any equivalent to such functionality in Spock?
I was trying to use PollingConditions to satisfy a similar scenario (which wasn't helpful), but instead found satisfaction in Spock's BlockingVariables. To verify that SomeService.method() is invoked at least once in function ClassBeingTested.method() within a given timeout period:
def "verify an interaction happened at least once within 200ms"(){
given:
def result = new BlockingVariable<Boolean>(0.2) // 200ms
SomeService someServiceMock = Mock()
someServiceMock.method() >> {
result.set(true)
}
ClassBeingTested clazz = new ClassBeingTested(someService: someServiceMock)
when:
clazz.someMethod()
then:
result.get()
}
When the result is set, the blocking condition will be satisfied and result.get() would have to return true for the condition to pass. If it fails to be set within 200ms, the test will fail with a timeout exception.
There is no equivalent in Spock. (PollingConditions can only be used for conditions, not for interactions.) The closest you can get is to add a sleep() statement in the then block:
when:
...
then:
sleep(200)
(1.._) * mock.baz()
Using PollingConditions and a boolean variable, the following example evaluates a function until it satisfies an interaction.
def "test the config watcher to reload games if a new customer directory is added"() {
given:
def conditions = new PollingConditions(initialDelay: 1, timeout: 60, factor: 1.25)
def mockGameConfigLoader = Mock(GameConfigLoader)
def gameConfigWatcher= new GameConfigWatcher(mockGameConfigLoader)
boolean gamesReloaded = false
when:
conditions.eventually {
gameConfigWatcher.listenEvents()
assert gamesReloaded
}
then:
1 * mockGameConfigLoader.loadAllGameConfigs() >> {
gamesReloaded = true
}
0 * _
}
This doesn't do exactly what the question asked, but I found it a bit cleaner that using a variable. If you have other conditions to asynchronously test in addition to the interaction, then you can declare the interactions at mock creation time and then use PollingConditions to test the other conditions. PollingConditions will either fail the test or block until the conditions pass, so that by the time the interaction is tested, the method should have been called:
#MicronautTest
class KernelManagerTest extends Specification {
#Inject
KernelManager kernelManager
//create conditions
PollingConditions conditions = new PollingConditions(timeout: 10, initialDelay: 1)
class Exits {
static createKernel (String[] args) {
System.exit(args[0].toInteger())
}
}
def "handles a system exit from within kernel"() {
given:
// set custom kernel
kernelManager.kernelClass = Exits
// create custom logger
kernelManager.log = Mock(Logger) {
1 * warn("Kernel exited unexpectedly.", _ as UnexpectedExitException)
}
when:
// create new kernel (exit 1)
kernelManager.startNewKernel("1")
then:
conditions.eventually {
kernelManager.kernelInstances.size() == 0
kernelManager.kernelThreads.size() == 0
}
}
}