Frustrating. Everywhere i look, i see samples of testing async Vertx code, but nothing that comes close to what i am trying to test.
Vertx 3.3.2, JUnit 4.12, Java 8
The method under test sends a message to the event bus. I want to verify that what happens in the eventBus().send() response handler is happening.
Sooooooo many examples i see have the eventBus().send() method in the TEST ITSELF (thus, testing the other end of the event bus - the consumer) I want to test the response handler in the .send()
I have tried Async in the test. Tried ArgumentCaptor. Tried Thread.sleep(). Tried doAnswer(). Nothing seems to get the test to (a) wait for the async eventBus().send() call in the method under test to finish and (b) able to verify() that there was an interaction (i think this might have to do with the different between the Vertx.TestContext and the JUnit.Runner Context..)
Code:
Method under test:
public void sendToEventBusAddress(RoutingContext context, String requestId, String userId) {
List<String> stuff = split(context.request().getParam("stuffToSplit"));
JsonObject eventBusMessage = new JsonObject()
.put("requestId", requestId)
.put("stuffList", new JsonArray(stuff))
.put("userId", userId);
LOGGER.info("Putting message: {} onto the EventBus at address: {}", eventBusMessage.encodePrettily(), EventBusEndpointEnum.STUFF_ACCESS.getValue());
context.vertx().eventBus().send(EventBusEndpointEnum.STUFF_ACCESS.getValue(), eventBusMessage, new DeliveryOptions().setSendTimeout(timeout), async -> {
if (async.succeeded()) {
LOGGER.info("EventBus Response: {}", async.result().body().toString());
context.response().setStatusCode(HttpStatus.SC_OK);
context.response().headers().set(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
context.response().end(async.result().body().toString());
} else {
LOGGER.error(errorMessage);
context.response().setStatusCode(HttpStatus.SC_BAD_REQUEST);
context.response().end(errorMessage);
}
});
}
Simplified (non-working) Test case and class:
#RunWith(VertxUnitRunner.class)
public class MyBrokenTest {
#Mock private RoutingContext routingContext;
#Mock private HttpServerRequest contextRequest;
#Mock private HttpServerResponse contextResponse;
#Mock private MultiMap responseHeaders;
#Rule public RunTestOnContext rule = new RunTestOnContext();
#Before
public void setUp(TestContext context) {
MockitoAnnotations.initMocks(this);
}
#Test
public void testOne(TestContext context) {
when(routingContext.vertx()).thenReturn(rule.vertx());
when(routingContext.request()).thenReturn(contextRequest);
when(contextRequest.getParam("stuffToSplit")).thenReturn("04MA");
when(routingContext.response()).thenReturn(contextResponse);
when(contextResponse.headers()).thenReturn(responseHeaders);
rule.vertx().eventBus().consumer(EventBusEndpointEnum.STUFF_ACCESS.getValue(), res -> {
res.reply("yo");
});
ClassUnderTest cut= new ClassUnderTest(180000);
cut.sendToEventBusAddress(routingContext, "testRequestId", "UnitTestId");
verify(contextResponse).setStatusCode(200);
}
}
I know that the test in its current form won't work, because the method under test returns as soon as the eventBus().send() method is called inside the method, and therefore, the verify fails with 'no interactions'.
What i can't figure out, is how to verify it properly given the async nature of Vertx!
Thanks
I did it so:
At #BeforeAll annotated method I deploy only the sending verticle.
At #BeforeEach - I create a consumer for the given message and store message(s) to variable/collection
Something like:
receivedMessage = new Message[1];
eventBus.consumer("DB",
message -> {
message.reply("OK");
receivedMessage[0] = message;
});
context.completeNow();
In test I validate stored value(s):
client.get(8080, "localhost", "/user/" + id)
.as(BodyCodec.string())
.send(context.succeeding((response -> context.verify(() -> {
Assertions.assertEquals(expectedMessage, receivedMessage[0].body());
context.completeNow();
}))));
Related
i am new to vert.x , i am trying to make unit testing by calling my webservices .
i need an example of testing webservices with testng.
when i did it with junit5 it works fine with the below code
#BeforeMethod
private static void deploy_verticle(Vertx vertx , VertxTestContext testContext) {
vertx.deployVerticle(new MainVerticle(), testContext.completing());
}
#Test
public void getTestFalse(Vertx vertx , VertxTestContext testContext) throws InterruptedException {
Assert.assertTrue("3".contains("tesdfsdf"));
req = new HttpRequestEntityTest();
req.setHost("localhost");
req.setPort(8089);
req.setUri("/api/data");
utilTest = new RestUtilTest();
response = new HttpResponseEntityTest();
utilTest.getTestUtil(req, testContext.succeeding(response -> {
testContext.verify(() -> {
Assert.assertTrue(response.getBodyStr().contains("tesdfsdf"));
});
testContext.completeNow();
}));
}
#AfterMethod
public void finish(Vertx vertx , VertxTestContext testContext) {
context.getVertx().close(context.getTestContext().succeeding(response -> {
context.getTestContext().completeNow();
}));
}
but when i try to work with TestNg like this :
#SuppressWarnings("deprecation")
#Test
public void getTestIqvia() throws InterruptedException {
Future<HttpResponseEntityTest> future = Future.future();
response = new HttpResponseEntityTest();
req = new HttpRequestEntityTest();
req.setHost("localhost");
req.setPort(8089);
req.setUri("/api/data");
utilTest = new RestUtilTest();
utilTest.getTestUtil(req, response -> {
System.out.println(response.result().getBodyStr());
System.out.println(response.result().getBodyStr().contains("dsds"));
Assert.assertTrue(response.result().getBodyStr().contains("fdsfds"));
});
GetTestUtil Function :
public Future<HttpResponseEntityTest> getTestUtil(HttpRequestEntityTest req,
Handler<AsyncResult<HttpResponseEntityTest>> resultHandler) {
future = Future.future();
WebClient client = WebClient.create(vertx);
response = new HttpResponseEntityTest();
client.get(req.getPort(), req.getHost(), req.getUri()).send(ar -> {
if (ar.succeeded()) {
response.setStatusCode(ar.result().statusCode());
response.setBodyStr(ar.result().bodyAsString());
future.complete(response);
System.out.println("Received response with status code get" + response.getStatusCode());
resultHandler.handle(Future.succeededFuture(response));
} else {
response.setErrorMsg(ar.cause().getMessage());
System.out.println("Something went wrong " + ar.cause().getMessage());
future.complete(response);
}
});
return future;
}
always sending to web client not entered in the arrow function ar and always test success because it doesn't go to Assert function . except fewtimes in debugging mode only
can any one has ana example in testNG with Vert.x?? or what's wrong in my code?
This happens because the getTestIqvia completes before the WebClient callback is invoked.
With jUnit5, the corresponding Vert.x test extension makes sure the test is not marked as passed until all the asynchronous assertions pass.
With TestNG you need to do this manually. In practice, you need to make the test wait until the future returned by getTestUtil is completed.
I am using hystrix javanica collapser in spring boot, but I found it did not work, my code just like this below:
service class:
public class TestService {
#HystrixCollapser(batchMethod = "getStrList")
public Future<String> getStr(String id) {
System.out.println("single");
return null;
}
#HystrixCommand
public List<String> getStrList(List<String> ids) {
System.out.println("batch,size=" + ids.size());
List<String> strList = Lists.newArrayList();
ids.forEach(id -> strList.add("test"));
return strList;
}
}
where I use:
public static void main(String[] args) {
TestService testService = new TestService();
HystrixRequestContext context = HystrixRequestContext.initializeContext();
Future<String> f1= testService.getStr("111");
Future<String> f2= testService.getStr("222");
try {
Thread.sleep(3000);
System.out.println(f1.get()); // nothing printed
System.out.println(f2.get()); // nothing printed
} catch (Exception e) {
}
context.shutdown();
}
It printed 3 single instead of 1 batch.
I want to know what's wrong with my code, a valid example is better.
I can't find a hystrix javanica sample on the internet, So I have to read the source code to solve this problem, now it's solved, and this is my summary:
when you use hystrix(javanica) collapser in spring-boot, you have to:
Defined a hystrixAspect spring bean and import hystrix-strategy.xml;
Annotate single method with #Hystrix Collapser annotate batch method with #HystrixCommand;
Make your single method need 1 parameter(ArgType) return Future , batch method need List return List and make sure size of args be equal to size of return.
Set hystrix properties batchMethod, scope, if you want to collapse requests from multiple user threads, you must set the scope to GLOBAL;
Before you submit a single request, you must init the hystrix context with HystrixRequestContext.initializeContext(), and shutdown the context when your request finish;
when I was sync I wrote unit tests mocking the persistence part and check the caller's behavior. Here is an example about what I usually did:
#Mock
private OfferPersistenceServiceImpl persistenceService;
#Inject
#InjectMocks
private OfferServiceImpl offerService;
...
#Test
public void createInvalidOffer() {
offer = new Offer(null, null, null, null, null, 4, 200D, 90D);
String expectedMessage = Offer.class.getName() + " is not valid: " + offer.toString();
Mockito.when(persistenceService.create(offer)).thenThrow(new IllegalArgumentException(expectedMessage));
Response response = offerService.create(offer);
Mockito.verify(persistenceService, Mockito.times(1)).create(offer);
Assert.assertEquals(INVALID_INPUT, response.getStatus());
String actualMessage = response.getEntity().toString();
Assert.assertEquals(expectedMessage, actualMessage);
}
But now I fell in love with Vertx.io (to which I am pretty new) and I want to be async. Nice. But Vertx has handlers, so the new persistence component to mock looks like this:
...
mongoClient.insert(COLLECTION, offer, h-> {
...
});
So I am guessing how to mock handler h to tests class who's using that mongoClient or even if it is the right way to test with Vertx.io. I am using vertx.io 3.5.0, junit 4.12 and mockito 2.13.0. Thanks.
Update
I tried to follow tsegimond suggestion but I can't get how Mockito's Answer and ArgumentCaptor can help me. Here is what I tried so far.
Using ArgumentCaptor:
JsonObject offer = Mockito.mock(JsonObject.class);
Mockito.when(msg.body()).thenReturn(offer);
Mockito.doNothing().when(offerMongo).validate(offer);
RuntimeException rex = new RuntimeException("some message");
...
ArgumentCaptor<Handler<AsyncResult<String>>> handlerCaptor =
ArgumentCaptor.forClass(Handler.class);
ArgumentCaptor<AsyncResult<String>> asyncResultCaptor =
ArgumentCaptor.forClass(AsyncResult.class);
offerMongo.create(msg);
Mockito.verify(mongoClient,
Mockito.times(1)).insert(Mockito.anyString(), Mockito.any(), handlerCaptor.capture());
Mockito.verify(handlerCaptor.getValue(),
Mockito.times(1)).handle(asyncResultCaptor.capture());
Mockito.when(asyncResultCaptor.getValue().succeeded()).thenReturn(false);
Mockito.when(asyncResultCaptor.getValue().cause()).thenReturn(rex);
Assert.assertEquals(Json.encode(rex), msg.body().encode());
and using Answer:
ArgumentCaptor<AsyncResult<String>> handlerCaptor =
ArgumentCaptor.forClass(AsyncResult.class);
AsyncResult<String> result = Mockito.mock(AsyncResult.class);
Mockito.when(result.succeeded()).thenReturn(true);
Mockito.when(result.cause()).thenReturn(rex);
Mockito.doAnswer(new Answer<MongoClient>() {
#Override
public MongoClient answer(InvocationOnMock invocation) throws Throwable {
((Handler<AsyncResult<String>>)
invocation.getArguments()[2]).handle(handlerCaptor.capture());
return null;
}
}).when(mongoClient).insert(Mockito.anyString(), Mockito.any(),
Mockito.any());
userMongo.create(msg);
Assert.assertEquals(Json.encode(rex), msg.body().encode());
And now I got confused. Is there a way to mock an AsyncResult to let it return false on succeed()?
Finally I got some times to investigate and I made it. Here is my solution.
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(VertxUnitRunner.class)
#PrepareForTest({ MongoClient.class })
public class PersistenceTest {
private MongoClient mongo;
private Vertx vertx;
#Before
public void initSingleTest(TestContext ctx) throws Exception {
vertx = Vertx.vertx();
mongo = Mockito.mock(MongoClient.class);
PowerMockito.mockStatic(MongoClient.class);
PowerMockito.when(MongoClient.createShared(Mockito.any(), Mockito.any())).thenReturn(mongo);
vertx.deployVerticle(Persistence.class, new DeploymentOptions(), ctx.asyncAssertSuccess());
}
#SuppressWarnings("unchecked")
#Test
public void loadSomeDocs(TestContext ctx) {
Doc expected = new Doc();
expected.setName("report");
expected.setPreview("loremipsum");
Message<JsonObject> msg = Mockito.mock(Message.class);
Mockito.when(msg.body()).thenReturn(JsonObject.mapFrom(expected));
JsonObject result = new JsonObject().put("name", "report").put("preview", "loremipsum");
AsyncResult<JsonObject> asyncResult = Mockito.mock(AsyncResult.class);
Mockito.when(asyncResult.succeeded()).thenReturn(true);
Mockito.when(asyncResult.result()).thenReturn(result);
Mockito.doAnswer(new Answer<AsyncResult<JsonObject>>() {
#Override
public AsyncResult<JsonObject> answer(InvocationOnMock arg0) throws Throwable {
((Handler<AsyncResult<JsonObject>>) arg0.getArgument(3)).handle(asyncResult);
return null;
}
}).when(mongo).findOne(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
Async async = ctx.async();
vertx.eventBus().send("persistence", new JsonObject(), msgh -> {
if (msgh.failed()) {
System.out.println(msgh.cause().getMessage());
}
ctx.assertTrue(msgh.succeeded());
ctx.assertEquals(expected, Json.decodeValue(msgh.result().body().toString(), Doc.class));
async.complete();
});
async.await();
}
}
Use Powemockito to mock the MongoClient.createShared static method, so you'll have your mock when verticle starts. Mocking async handler is a bit of code to write. As you can see mocking start at Message<JsonObject> msg = Mockito.mock(Message.class); and ends at Mockito.doAnswer(new Answer.... In the Answer's method pick the handler param and force it to handle your async result then you're done.
Normally, I'd use a comment to post this, but formatting gets lost. The accepted solution is works great, just note that it can be simplified a bit using Java 8+, and you can use your actual objects instead of JSON.
doAnswer((Answer<AsyncResult<List<Sample>>>) arguments -> {
((Handler<AsyncResult<List<Sample>>>) arguments.getArgument(1)).handle(asyncResult);
return null;
}).when(sampleService).findSamplesBySampleFilter(any(), any());
getArgument(1), refers to the index of the handler argument in a method such as:
#Fluent
#Nonnull
SampleService findSamplesBySampleFilter(#Nonnull final SampleFilter sampleFilter,
#Nonnull final Handler<AsyncResult<List<Sample>>> resultHandler);
I have a unit test (simplified version below) that tests a Netty handler.
I create an EmbeddedChannel with an instance of the handler.
The caller writes a string to the channel
The handler receives the string, reverses and writes it back.
The caller reads the return values from the channel and verifies it is the reverse of the sent string.
This works perfectly. However, I need to verify the number of invocations on the channel, so I created a spy of the channel but mocked no methods, since I don't want to change the behavior of the class, just count invocations.
Now the test fails. 2 of the assertions succeed. They are a test to make sure the handler was called, and a test to verify the number of times a method of the channel was called. However, the final read response is always null when the spy is used.
I was under the impression that a solitary spy with no other mocking would not affect the behavior of the spied object, but obviously it does. The [nonPower] Mockito docs indicate the objects are copied which might cause this issue, but the PowerMockito docs are not as specific.
I am using Netty 4.1.6.Final and Powermock 1.5.6.
UPDATE: I managed to get the test working but it's a bit of wonky workaround. See the new method testSpiedEmbeddedChannel2. The workaround is that I create a non-spied channel (ecx), then a the spied channel (ec) using ecx. I issued the write on ec, and the read using ecx. This means if I try to verify methods used in the read, they will not be counted.
Here's the code with the successful and failing tests.
#RunWith(PowerMockRunner.class)
#PowerMockIgnore({"javax.management.*"})
#PrepareForTest(EmbeddedChannel.class)
public class TestEmbeddedChannel {
class EchoHandler extends ChannelDuplexHandler {
final AtomicInteger reads = new AtomicInteger(0);
#Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
reads.incrementAndGet();
final String value = (String)msg;
final String response = new StringBuilder(value).reverse().toString();
ctx.channel().writeAndFlush(response);
}
}
#Test
public void testEmbeddedChannel() { // PASSES
final EchoHandler handler = new EchoHandler();
final EmbeddedChannel ec = new EmbeddedChannel(handler);
ec.writeInbound("Hello World");
final String response = ec.readOutbound();
Assert.assertEquals(1, handler.reads.get());
Assert.assertEquals("dlroW olleH", response);
}
#Test
public void testSpiedEmbeddedChannel() { // FAILS
final EchoHandler handler = new EchoHandler();
final EmbeddedChannel ec = spy(new EmbeddedChannel(handler));
ec.writeInbound("Hello World");
final String response = ec.readOutbound();
verify(ec, times(2)).isOpen(); // OK
Assert.assertEquals(1, handler.reads.get()); // OK
Assert.assertEquals("dlroW olleH", response); // FAILS
}
#Test
public void testSpiedEmbeddedChannel2() { // PASSES
final EchoHandler handler = new EchoHandler();
final EmbeddedChannel ecx = new EmbeddedChannel(handler);
final EmbeddedChannel ec = spy(ecx);
ec.writeInbound("Hello World");
final String response = ecx.readOutbound(); // Read using non-spied channel
verify(ec, times(2)).isOpen();
Assert.assertEquals(1, handler.reads.get());
Assert.assertEquals("dlroW olleH", response);
}
}
Thanks for any guidance here.
I have a function that processes events obtained by a PullSubscription to Microsoft Exchange.
public void processEvents(ExchangeService service, PullSubscription subscription)
throws Exception {
GetEventsResults events = subscription.getEvents();
// Loop through all item-related events.
for (ItemEvent itemEvent : events.getItemEvents()) {
if (itemEvent.getEventType() == EventType.NewMail) {
EmailMessage message = EmailMessage.bind(service, itemEvent.getItemId());
EmailParser emailParser = new EmailParser();
emailParser.parse(message, service);
}
}
}
I am trying to test it using PowerMockito because ExchangeService is a final class.
So I have mocked ExchangeService and PullSubscription as follows:
ExchangeService serviceMock = PowerMockito.mock(ExchangeService.class);
PullSubscription subscriptionMock = PowerMockito.mock(PullSubscription.class);
#Test
public void startPullNotification() throws Exception {
ProcessEmails pr = new ProcessEmails("config.properties");
pr.startPullNotification(serviceMock);
}
When I'm trying to test it using the following code it throws a NullPointerException because subscription.getEvents() returns null (i.e. the subscriptionMock has no events in it).
I tried stubbing it by mocking the eventResults that has to be returned:
when(subscriptionMock.getEvents()).thenReturn(eventResultsMock);
It doesn't work since the getEvents() is not called in the test function. I wanted to know how to test this function?
http://archive.msdn.microsoft.com/ewsjavaapi
Solution:
I had to mock every object being created in the function.
Also, I had to add the following above the class declaration.
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ClassBeingTested.class })