Mockito with JUnit
When you write/used to write test cases for your newly added function in your program, you may have come to the point where you realize the input for this function depends on the output of another program or system or resources, like http, database call etc. and we can not make the call because it is for tests. right? Calling real resources does not make sense either, or will you maintain all of these just to pass the tests you have written? Obviously not.
Here comes Mockito handy, with Mockito you can mock all most everything, and write codes to return object you wanted to.
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
Lets go through example to understand its working better.
Lets say, I have a socket echo server and client. The server echoes back everything client writes. And at client side you have to verify without making any call to the server. So how will you write your client side code?
The answer is you can mock the streams that you obtained with socket. Whenever you write to the socket, you use its output stream, we can use that.
- EchoClient.java
package test;
import java.io.IOException;
import java.net.Socket;
public class EchoClient {
final Socket socket;
public EchoClient(Socket socket) {
this.socket = socket;
}
public byte[] echo(byte[] message) throws IOException {
socket.getOutputStream().write(message);
byte[] messageRead = new byte[message.length];
int read = socket.getInputStream().read(messageRead);
if (read == -1) {
throw new IOException("no data read.");
}
return messageRead;
}
} - EchoClientTest.java
package test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@ExtendWith(MockitoExtension.class)
public class EchoClientTest {
@Mock Socket socket;
// this is same as: socket = Mockito.Mock(Socket.class)
@Mock InputStream inputStream;
@Mock OutputStream outputStream;
@Test
public void testEcho() throws IOException {
Mockito.when(socket.getInputStream()).thenReturn(inputStream);
Mockito.when(socket.getOutputStream()).thenReturn(outputStream);
byte[] message = "Hi".getBytes(StandardCharsets.UTF_8);
// this is to validate write
Mockito.doAnswer(
(invocationOnMock) -> {
byte[] param = invocationOnMock.getArgument(0);
Assertions.assertArrayEquals(message, param);
return null;
})
.when(outputStream)
.write(any(byte[].class));
// this is to validate read
Mockito.doAnswer(
(invocationOnMock) -> {
byte[] param = invocationOnMock.getArgument(0);
Assertions.assertEquals(message.length, param.length);
// copy the message
System.arraycopy(message, 0, param, 0, message.length);
return message.length;
})
.when(inputStream)
.read(any(byte[].class));
EchoClient echoClient = new EchoClient(socket);
byte[] received = echoClient.echo(message);
// this validates echo
Assertions.assertArrayEquals(message, received);
// also with this approach
// you will have to validate the outputstream and inputsteam were in use
Mockito.verify(outputStream, Mockito.times(1)).write(any(byte[].class));
Mockito.verify(inputStream, Mockito.times(1)).read(any(byte[].class));
}
}
Comments
Post a Comment