I know that WorkManager provides a work-testing artifact for test workers and we can use TestListenableWorkerBuilder to test CoroutineWorker (see this link for more information). I found an medium article by Ian Roberts showing how to test CoroutineWorker with dependencies by creating your own WorkerFactory.
According to official documentation, we can test initial delays on Worker using TestDriver but nothing was said about testing delays, constraints etc on CoroutinesWork. Is there a way to perform such tests in CoroutineWorker using TestListenableWorkerBuilder?
After watching this video (at 13:00) from the 2019 Android Dev Summit, I found the answer for this question:
When initializing workManager for test (by WorkManagerTestInitHelper.initializeTestWorkManager method), we have to pass our custom WorkerFactory through the configuration step;
Setup your request worker as you normally would using the OneTimeWorkRequestBuilder method;
By default, all constraints for Workmanager instances in the test
mode are unmet. Using an instance of TestDriver, we can mark those constraints as met.
Here's an example to summarize these above steps:
#Test
fun checkInitialDelay() {
val config = Configuration.Builder()
.setWorkerFactory(
MyWorkFactory(myDependencies)
)
.setMinimumLoggingLevel(Log.DEBUG)
.setExecutor(SynchronousExecutor())
.build()
// Initialize WorkManager
WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
//setup the request work
val request =
OneTimeWorkRequestBuilder<MyWork>()
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
val workManager = WorkManager.getInstance(context)
// Get the TestDriver
val testDriver = WorkManagerTestInitHelper.getTestDriver(context)
// Enqueue
workManager.enqueue(request).result.get()
// Tells the WorkManager test framework that initial delays are now met.
testDriver?.setInitialDelayMet(request.id)
// Get WorkInfo and outputData
val workInfo = workManager.getWorkInfoById(request.id).get()
// Assert
assert(workInfo.state == WorkInfo.State.SUCCEEDED)
}
Related
I have a function in my ViewModel in which I subscribe to some updates, I want to write a test that will check that after the subscribe is triggered, the specific function is called from the subscribe.
Here is how the function looks:
fun subscribeToTablesUpdates() {
dataManager.getTablesList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { tablesList ->
updateTablesState(tablesList)
}
}
And this is the test that I wrote:
#Test
fun subscribeToTablesListTest() {
val mockedTablesList = mockk<List<Table>()
every {
viewModel.dataManager.getTablesList()
} returns Observable.just(mockedTablesList)
viewModel.subscribeToTablesUpdates()
verify {
viewModel.updateTablesState(mockedTablesList)
}
}
The issue is that I receive assertion exception without any another info and I don't know how to fix that.
Edit 1: subscribeToTableUpdates() is calling from the init block of ViewModel.
So basically the test itself was done right, but there were linking issue. Since the function of the VM was called from the init block the subscription happened only once, and that created a situation when at the time when I mocked the data service, the observer was already subscribed to the other service. Since the init block is called only once, there is no way to change the implementation of the data service to that observer.
After all this investigation the one thing which I successfully forgot came to my mind again: extract every external dependencies to constructors, so further you could substitute it for the test without any problems like this.
I'm currently working with:
Micronaut 3.7.3
RabbitMQ 3.11.2
Spock
Groovy / Java 17
I'm implementing a rabbitmq consumer for a simple demo project following the guidelines from micronaut project (https://micronaut-projects.github.io/micronaut-rabbitmq/3.1.0/guide/index.html)
I'm trying to mock a service that is a dependency of my rabbitmq consumer.
I've tried this approach that does not seem to work:
#MicronautTest
#Subject(SampleRequestConsumer)
class SampleRequestConsumerSpec extends Specification {
#Inject
ExternalWorkflowProducer externalWorkflowProducer
#Inject
SampleRequestConsumer sampleRequestConsumer
#Inject
SimpleService simpleService
#MockBean(SimpleService)
SimpleService simpleService() {
Mock(SimpleService)
}
def "It receives a sampleRequest message in the simple.request queue"() {
when:
externalWorkflowProducer.send(new SampleRequest(message: "Request1"))
then:
sleep(100)
1 * simpleService.handleSimpleRequest(_ as SampleRequest) >> { SampleRequest request ->
assert request.message != null
}
}
}
I get this error when running the integration test:
Too few invocations for:
1 * simpleService.handleSimpleRequest(_ as SampleRequest) >> { SampleRequest request ->
assert request.message != null
} (0 invocations)
Unmatched invocations (ordered by similarity):
None
See full source code on GitHub: https://github.com/art-dambrine/micronaut-rabbitmq-spock-mocking/blob/test-with-mq/src/test/groovy/com/company/microproject/amqp/consumer/SampleRequestConsumerSpec.groovy
Also notice that when I'm not reading from the queue and directly calling the method sampleRequestConsumer.receive([message: "Request1"])
mocking for the simpleService is working : https://github.com/art-dambrine/micronaut-rabbitmq-spock-mocking/blob/test-without-mq/src/test/groovy/com/company/microproject/amqp/consumer/SampleRequestConsumerSpec.groovy
Thanks in advance for your insight
IMPORTANT
Please use the branch test-with-mq. The branch test-without-mq's tests will succeed because it's not using rabbitMQ. This is an attempt to demonstrate that the issue lies in testing RabbitMQ consumers.
Moving the sleep() instruction to the when: block fixed the test.
Indeed, what is behind the then: block is not executed after the externalWorkflowProducer.send(), it is executed before by Spock.
This instruction:
1 * simpleService.handleSimpleRequest(_ as SampleRequest)
is creating an interaction in the scope of the feature method, and it is executed when the specification is configured.
Adding a sleep() instruction there, is not leaving the spec time for the consumer to receive the message. You need to add the sleep() after the send(). This is when your test needs to let the consumer time to execute.
Note: the closure itself:
{ SampleRequest request ->
assert request.message != null
}
would be executed afterwards, but only the closure. The sleep instruction was already executed when configuring the Spec. The Closure is not executed in this case, because the test finishes before the thread of the consumer can invoke the mock.
In summary:
This works:
def "It receives a sampleRequest message in the simple.request queue"() {
when:
externalWorkflowProducer.send(new SampleRequest(message: "Request1"))
sleep(100)
then:
1 * simpleService.handleSimpleRequest(_ as SampleRequest) >> { SampleRequest request ->
assert request.message != null
}
}
And this doesn't work:
def "It receives a sampleRequest message in the simple.request queue"() {
when:
externalWorkflowProducer.send(new SampleRequest(message: "Request1"))
then:
sleep(100)
1 * simpleService.handleSimpleRequest(_ as SampleRequest) >> { SampleRequest request ->
assert request.message != null
}
}
As #LuisMuñiz pointed out, the interactions declared in the then block are actually moved around. It creates an interaction scope that contains all the interactions, the setup of that happens immediately before the when block executes and the verification that all interactions had taken place happens before any other instruction in the then block.
That out of the way, I would advise against using any kind of sleeps for your code. At best you are just waiting uselessly, at worst you didn't wait long enough and you test breaks. It is preferable to use one or more CountDownLatch instances to synchronize your test.
def "It receives a sampleRequest message in the simple.request queue"() {
given:
def latch = new CountDownLatch(1)
when:
externalWorkflowProducer.send(new SampleRequest(message: "Request1"))
latch.await()
then:
1 * simpleService.handleSimpleRequest(_ as SampleRequest) >> { SampleRequest request ->
assert request.message != null
latch.countDown()
}
}
This way you test will wait until you mock was called, but then immediately finish.
You can also use latch.await(long timeout, TimeUnit unit) with a generous timeout, to guard against your test hanging indefinitely.
I'm actually confused on assembly time and subscription time. I know mono's are lazy and does not get executed until its subscribed. Below is a method.
public Mono<UserbaseEntityResponse> getUserbaseDetailsForEntityId(String id) {
GroupRequest request = ImmutableGroupRequest
.builder()
.cloudId(id)
.build();
Mono<List<GroupResponse>> response = ussClient.getGroups(request);
List<UserbaseEntityResponse.GroupPrincipal> groups = new CopyOnWriteArrayList<>();
response.flatMapIterable(elem -> elem)
.toIterable().iterator().forEachRemaining(
groupResponse -> {
groupResponse.getResources().iterator().forEachRemaining(
resource -> {
groups.add(ImmutableGroupPrincipal
.builder()
.groupId(resource.getId())
.name(resource.getDisplayName())
.addAllUsers(convertMemebersToUsers(resource))
.build());
}
);
}
);
log.debug("Response Object - " + groups.toString());
ImmutableUserbaseEntityResponse res = ImmutableUserbaseEntityResponse
.builder()
.userbaseId(id)
.addAllGroups(groups)
.build();
Flux<UserbaseEntityResponse.GroupPrincipal> f = Flux.fromIterable(res.getGroups())
.parallel()
.runOn(Schedulers.parallel())
.doOnNext(groupPrincipal -> getResourcesForGroup((ImmutableGroupPrincipal)groupPrincipal, res.getUserbaseId()))
.sequential();
return Mono.just(res);
}
This gets executed Mono<List<GroupResponse>> response = ussClient.getGroups(request); without calling subscribe, however below will not get executed unless I call subscribe on that.
Flux<UserbaseEntityResponse.GroupPrincipal> f = Flux.fromIterable(res.getGroups())
.parallel()
.runOn(Schedulers.parallel())
.doOnNext(groupPrincipal -> getResourcesForGroup((ImmutableGroupPrincipal)groupPrincipal, res.getUserbaseId()))
.sequential();
Can I get some more input on assembly time vs subscription?
"Nothing happens until you subscribe" isn't quite true in all cases. There's three scenarios in which a publisher (Mono or Flux) will be executed:
You subscribe;
You block;
The publisher is "hot".
Note that the above scenarios all apply to an entire reactive chain - i.e. if I subscribe to a publisher, everything upstream (dependent on that publisher) also executes. That's why frameworks can, and should call subscribe when they need to, causing the reactive chain defined in a controller to execute.
In your case it's actually the second of these - you're blocking, which is essentially a "subscribe and wait for the result(s)". Usually the methods that block are clearly labelled, but again that's not always the case - in your case it's the toIterable() method on Flux doing the blocking:
Transform this Flux into a lazy Iterable blocking on Iterator.next() calls.
But ah, you say, I'm not calling Iterator.next() - what gives?!
Well, implicitly you are by calling forEachRemaining():
The default implementation behaves as if:
while (hasNext())
action.accept(next());
...and as per the above rule, since ussClient.getGroups(request) is upstream of this blocking call, it gets executed.
I recently started working with corda and try to create POC. There is one requirement is to create corda node at runtime. I have searched into corda documentation, but bad luck.
Is there any way to create nodes at runtime?
You could have a look at net.corda.testing.driver which can be used to start up a set of test nodes for integration testing
class DriverBasedTest {
private val bankA = TestIdentity(CordaX500Name("BankA", "", "GB"))
private val bankB = TestIdentity(CordaX500Name("BankB", "", "US"))
#Test
fun `node test`() = withDriver {
// Start a pair of nodes and wait for them both to be ready.
val (partyAHandle, partyBHandle) = startNodes(bankA, bankB)
// From each node, make an RPC call to retrieve another node's name from the network map, to verify that the
// nodes have started and can communicate.
// This is a very basic test: in practice tests would be starting flows, and verifying the states in the vault
// and other important metrics to ensure that your CorDapp is working as intended.
assertEquals(bankB.name, partyAHandle.resolveName(bankB.name))
assertEquals(bankA.name, partyBHandle.resolveName(bankA.name))
}
}
From here -> https://github.com/corda/corda-settler/blob/master/cordapp/src/integrationTest/kotlin/com/template/DriverBasedTest.kt
I have used Execution and Task Listener in my process. How to unit test them using Junit in Camunda.
You could use for example the Camunda Model API and write a unit test to test your Execution listener.
The unit test could look like the following:
#Test
public void testEndExecutionListenerIsCalledOnlyOnce() {
BpmnModelInstance modelInstance = Bpmn.createExecutableProcess("process")
.startEvent()
.userTask()
.camundaExecutionListenerClass(ExecutionListener.EVENTNAME_END, TestingExecutionListener.class.getName())
.endEvent()
.done();
testHelper.deploy(modelInstance);
// given
ProcessInstance procInst = runtimeService.startProcessInstanceByKey("process");
TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(procInst.getId());
//when task is completed
taskService.complete(taskQuery.singleResult().getId());
// then end listener is called
// assert something for example a variable is set or something else
}
For more examples see how Camunda tests the Execution Listeners in
ExecutionListenerTest.java.