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.
Related
/*
this method will create the required manifest file in compatible format such that
quicksight can import data from specified s3 bucket
*/
private JSONObject CreateManifestFileJSONObject(JSONObject ManifestFile){
JSONArray URIPrefixArray= new JSONArray();
URIPrefixArray.put(PrefixLocation);
JSONObject URIPrefixJSONObject= new JSONObject();
URIPrefixJSONObject.put("URIPrefixes",URIPrefixArray);
JSONArray FileLocationsArray= new JSONArray();
FileLocationsArray.put(URIPrefixJSONObject);
JSONObject globalUploadSettings= new JSONObject();
globalUploadSettings.put("format","JSON");
ManifestFile.put("globalUploadSettings",globalUploadSettings);
ManifestFile.put("fileLocations",FileLocationsArray);
return(ManifestFile);
}
/*
this method will upload the ManifestFile to same S3 Bucket in which data files is stored
*/
private void UploadManifestFileJSONObjectToS3(JSONObject ManifestFile){
try {
AmazonS3 S3Client = new Utility().SetUpS3Client();
byte[] fileContentBytes = (ManifestFile.toString()).getBytes();
InputStream fileInputStream = new ByteArrayInputStream(fileContentBytes);
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(fileContentBytes.length);
S3Client.putObject(new PutObjectRequest(Bucket_Name, ManifestFileName, fileInputStream, objectMetadata).withCannedAcl(CannedAccessControlList.PublicRead));
}
catch(Exception exception){
exception.printStackTrace();
}
}
public void handler() {
System.out.println("inside the manifest file");
try {
JSONObject ManifestFile = new JSONObject();
ManifestFile = CreateManifestFileJSONObject(ManifestFile);
UploadManifestFileJSONObjectToS3(ManifestFile);
}
catch(Exception exception){
exception.printStackTrace();
}
I want to test handler method but handlor method is calling private methods so I do not know how to write the test class for this code.
i want to write unit test for this class please help
this is the test class I am able to create up to this point but it will not surely mock s3 behaviour
#Test
public void handler() {
ManifestFileHandler manifestFileHandler=new ManifestFileHandler();
manifestFileHandler.handler();
}
You can test private methods with the help of PowerMock provides utilities which uses reflection to do certain things.
check the below example,
https://examples.javacodegeeks.com/core-java/mockito/mockito-mock-private-method-example-with-powermock/
There are two schools of thought on unit testing private functions. The first is that you make them public (or protected or package accessible) and test them as you would a public function. The second is that if they are private they are part of the encapsulated implementation detail and you only need to test them through the public functions.
My personal view is that complicated private functions are often a sign that you are breaking the single responsibility principle and it's likely you should have logic in private functions that should be split into a separate class that can then be tested through its public methods.
With respect to the code you've posted you have a larger problem than how to test the private functions: your class depends on other classes that you don't have control over. You have no way of mocking the behaviour of those classes to test various scenarios or to verify that they have been called correctly. I suspect it is this problem that is really behind your question.
As an example, I would suggest you inject a S3Client into your class rather than create it internally through new Utility().SetUpS3Client(). That way you can mock its behaviour and verify it is called correctly by your code. Attempting to do that with the real version of this class will be challenging.
So using this model, your code might look something like:
public class ManifestFileHandler {
private final S3Client client;
public ManifestFileHandler(S3Client client) {
this.client = client;
}
private void upload(JSONObject manifestFile) {
...
client.putObject(...);
}
public void handleManifest() {
...
upload(manifestFile);
...
}
}
And your test code (using mockito):
#Test
void testManifestUpload() {
S3Client client = mock(S3Client.class);
ManifestFileHandler handler = new ManifestFileHandler(client);
handler.handleManifest();
verify(client).putObject(expectedObject);
}
If you need to capture the argument passed to putObject and assert various aspects of it then that is possible with most mocking tools (including mockito) but is beyond the scope of your question.
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();
}))));
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'm writing unit tests in java using Mockito/PowerMockito, but on the test I'm working on, I can't get rid of this UnfinishedStubbingException.
The method I'm trying to test is private, so I use WhiteBoxImpl to invoke the method. Inside the method I invoke, a call is potentially made to another private method (call it pm2) in the class under test. I want to verify that pm2 is never called, so I make a spy for the class under test, and verify pm2 is never() called.
So far, this test has always thrown an UnfinishedStubbingException, but I can't figure out what part of my test Powermockito doesn't like. I have another (working) test that operates very similarly, except I don't need to verify the behavior of a method like pm2. So in this working case, I don't need to create a spy for the class under test. I believe my issue is somehow related to the spy, but I don't know of a way to test what I'm trying to test without it.
Here's what I have right now:
#Mock(name = "BO")
BO BOMock;
#Mock(name = "DAO")
DAOI DAOMock;
#InjectMocks
ServiceImpl service;
#Test
public void unitTest(){
MessageObject msg = new MessageObject();
Record recordMock = mock(Record.class);
MetaData metaDataMock = mock(MetaData.class);
doNothing().when(DAOMock).doAction(any(Param1.class), anyInt());
when(DAOMock.doOtherAction(any(Param1.class), eq(msg.getId()))).thenReturn(recordMock);
when(BOMock.getMetaData(anyInt(), anyInt()).thenReturn(metaDataMock);
ServiceImpl spy = PowerMockito.spy(this.service);
PowerMockito.doReturn(new Long(10)).when(spy, "checkDelay", recordMock, msg, metaDataMock);
Whitebox.invokeMethod(spy, "process", msg);
verify(recordMock, never()).getStatus();
}
Here's the method in the class ServiceImpl that I'm testing:
private BO BO = new BO();
private DAOI DAO = new DAO();
private void process(Message msg) {
try {
DAO.doAction(new Param1.class, msg.getId());
} catch(Exception e) {
logger.error("some message");
return;
}
Record record = null;
try {
int intParam1 = msg.getId();
int intParam2 = msg.getDifferentId();
MetaData metaData = BO.getMetaData(intParam1, intParam2);
record = DAO.loadRecord(new Param1(), msg.getId());
// checkDelay is a private method in ServiceImpl.java
long delayForMinutes = checkDelay(record, msg, metaData);
if(delayForMinutes > 0) {
// Control should reach here
logger.debug("some message");
return;
}
// Control should not reach here
if(Record != null && Record.getStatus() != CREATED) {
logger.debug("some message");
return;
}
// Perform various actions
} catch(Exception e) {
// Perform other various actions
}
}
When I run this test I get an UnfinishedStubbingException. The line at the top of the stack trace is:
DAO.doAction(new Param1.class, msg.getId());
The error message provides the following hints:
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
But I can't seem to figure out how any of them apply to my situation. Does anyone know what's going on behind the scenes to cause this error?
Thank you
The problem line is
when(DAOMock.doOtherAction(any(Param1.class), eq(msg.getId()))).loadRecord(recordMock);
You don't seem to have a then, thenReturn or thenThrow here. You always need to use one of those with when.
If I have an SUT which handles an exception using a try/catch block, as follows:
public static void methodToBeTested() {
...
try {
desktop.browse(new URI("www.google.com"));
} catch (IOException e) {
//Display message to user and log out entry in app logs
}
...
}
Question is that should I test the condition from my unit tests that the IOException is thrown? (The method under test launches a URI in the default browser)
If yes, since I am not throwing the exception from this method, how do i unit test this condition when the desktop.browse() threw an IOException?
Any thoughts or suggestions? I am using JMock
Thanks!
Basically what you want to do is to
mockup Desktop and whenever you send a browse message to it (no matter what URI is used), instead of hitting that URI, it should throw an IOException.
I have used Jmock long time ago. JMock as far as I remember, has some limitations, for exmaple it does not provide a mechanism for mocking static methods. And I am not sure how easy it is to mock your browser class in jmock world.
However it is almost trivial to test this using jmockit, which supports all sorts of fancy mocking mechanisms (including static references, singletons etc). (I am mentioning jmockit because no matter what your browse class is, jmockit can mock it.)
Below is an excerpt from an example from their website:
package jmockit.tutorial.domain;
import org.apache.commons.mail.*;
import jmockit.tutorial.persistence.*;
import org.junit.*;
import mockit.*;
public final class MyBusinessService_ExpectationsAPI_Test
{
#Mocked(stubOutClassInitialization = true) final Database unused = null;
#Mocked SimpleEmail anyEmail;
#Test
public void doBusinessOperationXyz() throws Exception
{
final EntityX data = new EntityX(5, "abc", "abc#xpta.net");
final EntityX existingItem = new EntityX(1, "AX5", "someone#somewhere.com");
new Expectations() {{
(1) Database.find(withSubstring("select"), any);
result = existingItem; // automatically wrapped in a list of one item
}};
new MyBusinessService(data).doBusinessOperationXyz();
(2) new Verifications() {{ Database.persist(data); }};
(4) new Verifications() {{ email.send(); times = 1; }};
}
#Test(expected = EmailException.class)
public void doBusinessOperationXyzWithInvalidEmailAddress() throws Exception
{
new Expectations() {{
(3) email.addTo((String) withNotNull()); result = new EmailException();
}};
EntityX data = new EntityX(5, "abc", "someone#somewhere.com");
new MyBusinessService(data).doBusinessOperationXyz();
}
}
Above is the class under test and below is a the test which specifically tests (3) part of the above code. I think it is similar to what you are trying to do. Check it out please.
#Test(expected = EmailException.class)
public void doBusinessOperationXyzWithInvalidEmailAddress() throws Exception
{
new MockUp<Email>() {
#Mock
(3) Email addTo(String email) throws EmailException
{
assertNotNull(email);
throw new EmailException();
}
};
new MyBusinessService(data).doBusinessOperationXyz();
}
}
If you want to stick to jmock, it is fine. But then you need to give us more info about Desktop class and its browse method so that we can think about what we can do in jmock world.