How to mock local variables using mockito or powermock - java

I have scenario like this
InputStreamReader reader = new InputStreamReader(getFileAsStream(resourceResolver, iconpath));
BufferedReader bReader = new BufferedReader(reader);
I have mocked till this point
getFileAsStream(resourceResolver, iconpath)
now I am getting one reader
BufferedReader bReader = new BufferedReader(reader);
but when I execute this line I get null and not able to move forward
while ((iconEntry = bReader.readLine()) != null)
Please tell me how can I mock this.
Please note I cannot change my main code therefore the solution present on Mockito docs is not valid in my case
Test code
#RunWith(PowerMockRunner.class)
#PrepareForTest({ FrameworkUtil.class, LoggerFactory.class })
public class IconPreviewServletTest {
private IconPreviewServlet iconPreviewServlet;
private SlingHttpServletRequest request;
private SlingHttpServletResponse response;
private Bundle bundle;
private BundleContext bundleContext;
private ServiceReference factoryRef;
private CommonService resolverFactory;
private PrintWriter out;
private ResourceResolver resourceResolver;
private Resource resource;
private Node node;
private Node jcrContent;
private javax.jcr.Property property;
private Binary binary;
private InputStream stream;
private InputStreamReader inputReader;
private BufferedReader reader;
#Before
public void setUp() throws IOException, PathNotFoundException,
RepositoryException {
init();
}
private void init() throws IOException, PathNotFoundException,
RepositoryException {
request = mock(SlingHttpServletRequest.class);
response = mock(SlingHttpServletResponse.class);
bundleContext = mock(BundleContext.class);
factoryRef = mock(ServiceReference.class);
resolverFactory = mock(CommonService.class);
out = mock(PrintWriter.class);
resourceResolver = mock(ResourceResolver.class);
resource = mock(Resource.class);
node = mock(Node.class);
jcrContent = mock(Node.class);
property = mock(Property.class);
binary = mock(Binary.class);
stream=IOUtils.toInputStream("some test data for my input stream");
reader = mock(BufferedReader.class);
inputReader=mock(InputStreamReader.class);
bundle = mock(Bundle.class);
mockStatic(FrameworkUtil.class);
mockStatic(LoggerFactory.class);
Logger log = mock(Logger.class);
when(LoggerFactory.getLogger(IconPreviewServlet.class)).thenReturn(log);
when(FrameworkUtil.getBundle(CommonService.class)).thenReturn(bundle);
when(bundle.getBundleContext()).thenReturn(bundleContext);
when(bundleContext.getServiceReference(CommonService.class.getName()))
.thenReturn(factoryRef);
when(bundleContext.getService(factoryRef)).thenReturn(resolverFactory);
when(request.getParameter("category")).thenReturn("category");
when(request.getParameter("query")).thenReturn("query");
when(response.getWriter()).thenReturn(out);
when(request.getResourceResolver()).thenReturn(resourceResolver);
when(
resourceResolver
.getResource("/etc/designs/resmed/icons/category/icons.txt"))
.thenReturn(resource);
when(resource.adaptTo(Node.class)).thenReturn(node);
when(node.getNode("jcr:content")).thenReturn(jcrContent);
when(jcrContent.getProperty("jcr:data")).thenReturn(property);
when(property.getBinary()).thenReturn(binary);
when(binary.getStream()).thenReturn(stream);
}

To make this work, you need to use Powermockito to intercept the constructor calls (new InputStreamReader(...), new BufferedReader(...)) so that your mocks get returned. An example is below. In your case, just intercepting the new BufferedReader call may be enough.
Assume the following is the code you want to test:
package test;
import java.io.*;
public class SUT {
public String doSomething() throws IOException {
InputStreamReader reader =
new InputStreamReader(getFileAsStream(null, null));
BufferedReader bReader =
new BufferedReader(reader);
return bReader.readLine();
}
private InputStream getFileAsStream(Object resourceResolver, Object iconPath)
throws FileNotFoundException {
return new ByteArrayInputStream("".getBytes());
}
}
The following test code is an example of how to intercept the constructor calls:
package test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.doReturn;
import static org.powermock.api.mockito.PowerMockito.mock;
#RunWith(PowerMockRunner.class)
#PrepareForTest({ SUT.class })
public class SUTTest {
#Test
public void doSomethingReturnsValueFromBufferedReader() throws Exception {
// Arrange
SUT sut = new SUT();
InputStreamReader inputStreamReaderMock = mock(InputStreamReader.class);
BufferedReader bufferedReaderMock = mock(BufferedReader.class);
// Set your mocks up to be returned when the new ...Reader calls
// are executed in sut.doSomething()
PowerMockito.whenNew(InputStreamReader.class).
withAnyArguments().thenReturn(inputStreamReaderMock);
PowerMockito.whenNew(BufferedReader.class).
withArguments(inputStreamReaderMock).
thenReturn(bufferedReaderMock);
// Set the value you want bReader.readLine() to return
// when sut.doSomething() executes
final String bufferedReaderReturnValue = "myValue";
doReturn(bufferedReaderReturnValue).when(bufferedReaderMock).readLine();
// Act
String result = sut.doSomething();
// Assert
assertEquals(bufferedReaderReturnValue, result);
}
}
This hopefully helps you in your immediate problem. However, it seems to me that what you're creating will be a very slow, confusing and brittle test. Right now, you're mocking so much that it makes very hard to determine what you're actually trying to test.
The high amount of mocking probably indicates that the design of the code you're testing would need some work to improve testability. If you can't touch the code, then you can't - but try to make your test more readable and intuitive ("When this method is invoked, this thing should happen, because...").

to make this line work:
while ((iconEntry = bReader.readLine()) != null)
you must determine how many lines you want to read and add this to your test code:
when(bReader.readLine()).thenReturn("line number one").thenReturn("line number two");

Related

How to mock Gson in mockito

In my application, there are few REST resources been invoked and I am using Gson as the library for parsing the responses received. While writing unit tests for the above methods I am unable to mock the Gson class as it is a final class.
While doing some research over the internet I found that a file named org.mockito.plugins.MockMaker should be created at src/test/resources/mockito-extensions with the content of the following,
mock-maker-inline
but still am unable to get it worked. What I am doing wrong.
I am getting the following exception when running the above test case (due to the gson object is not being properly mocked)
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176)
at com.google.gson.Gson.fromJson(Gson.java:803)
at com.google.gson.Gson.fromJson(Gson.java:768)
at com.google.gson.Gson.fromJson(Gson.java:717)
at com.google.gson.Gson.fromJson(Gson.java:689)
at org.kasun.sample.client.supportjira.impl.GroupRestClientImpl.addUser(GroupRestClientImpl.java:104)
at org.kasun.sample.client.GroupRestClientImplTest.addUserToAGroup(GroupRestClientImplTest.java:102
Please find my classes as follows,
Class been tested:
import com.google.gson.Gson;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import org.testing.kasun.client.supportjira.dto.SaveResult;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class GroupRestClientImpl{
private Gson gson = new Gson();
#Override
public SaveResult addUser(User user, Group group) {
WebResource resource = client.resource(baseUri + "/" + GROUP_URI_PREFIX + "/user?groupname=" + group.getName());
ClientResponse response = resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON)
.post(ClientResponse.class, user);
String jsonText;
if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
jsonText = response.getEntity(String.class);
JiraError jiraError = gson.fromJson(jsonText, JiraError.class);
throw new JiraException(jiraError.toString());
}
jsonText = response.getEntity(String.class);
SaveResult saveResults = gson.fromJson(jsonText, SaveResult.class);
return saveResults;
}
}
Test Classes:
class TestBase {
static final String JIRA_API_URL = "http://www.jira.com/jira/rest/api/2";
static final String MEDIA_TYPE_JSON = MediaType.APPLICATION_JSON;
#Mock
Client client;
#Mock
WebResource webResource;
#Mock
WebResource.Builder webResourceBuilder;
#Mock
ClientResponse clientResponse;
#Mock
Gson gson;
void setupMocks(Class<?> postPayloadType) {
initMocks(this);
when(client.resource(anyString())).thenReturn(webResource);
when(webResource.accept(anyString())).thenReturn(webResourceBuilder);
when(webResourceBuilder.type(anyString())).thenReturn(webResourceBuilder);
when(webResourceBuilder.get(eq(ClientResponse.class))).thenReturn(clientResponse);
when(webResourceBuilder.post(eq(ClientResponse.class), any(postPayloadType))).thenReturn(clientResponse);
when(clientResponse.getEntity(eq(String.class))).thenReturn("responseText");
}
#AfterMethod
protected void clearMocks() {
reset(client);
reset(webResource);
reset(webResourceBuilder);
reset(clientResponse);
reset(gson);
}
}
public class GroupRestClientImplTest extends TestBase {
private static final String JIRA_GROUP_API_URL = JIRA_API_URL + "/group";
private static final String JIRA_GROUP_MEMBER_API_URL = JIRA_GROUP_API_URL + "/member?groupname=";
private static final String JIRA_GROUP_MEMBER_ADD_API_URL = JIRA_GROUP_API_URL + "/user?groupname=";
private GroupRestClient groupRestClient;
#BeforeMethod
public void initialize() throws URISyntaxException {
super.setupMocks(Group.class);
groupRestClient = new GroupRestClientImpl(new URI(JIRA_API_URL), client);
}
#Test
public void addUserToAGroup() throws URISyntaxException {
when(clientResponse.getStatus()).thenReturn(Response.Status.CREATED.getStatusCode());
when(webResourceBuilder.post(eq(ClientResponse.class), any(User.class))).thenReturn(clientResponse);
SaveResult saveResult = new SaveResult();
when(gson.fromJson(anyString(), isA(SaveResult.class.getClass()))).thenReturn(saveResult);
// when(gson.fromJson(anyString(), eq(SaveResult.class))).thenReturn(saveResult);
User user = new User();
Group group = new Group();
group.setName("group");
SaveResult result = groupRestClient.addUser(user, group);
// Test if the SaveResult is correct.
Assert.assertEquals(result, saveResult);
}
According to Mockito's documentation, this is feature is built around Java 9.
This mock maker has been designed around Java Agent runtime attachment ; this require a compatible JVM, that is part of the JDK (or Java 9 VM).
If you have a version prior to 9, you can:
When running on a non-JDK VM prior to Java 9, it is however possible to manually add the Byte Buddy Java agent jar using the -javaagent parameter upon starting the JVM.

Testing URL and URLConnection

I don't know how should I test this without really taking connection to real url to server.
I have read few articles about using Mockito in this kind of situation and tried to search around, but can not find good tutorial or advices how should I make jUnit-test for URL and URLConnection in my project.
Here is the code that I have problems when trying to test it:
public JSONObject getJSONObj()
throws MalformedURLException, IOException, ParseException {
String jsonString;
try (InputStream is = getURLConnection("RealUrlStringGoesHere").getInputStream();) {
jsonString = IOUtils.toString(is);
}
return (JSONObject) JSONValue.parseWithException(jsonString);
}
public URLConnection getURLConnection(String urlStr) throws MalformedURLException, IOException {
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
return conn;
}
Here is also used imports I use for these, if someone wants to know:
import java.net.URLConnection;
import org.apache.commons.io.IOUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
EDITED
Thanks for you answers, but it seems that I'm totally lost with this. Maybe I'm trying to think too complicated, but unit testing is pretty new stuff for me, but really want to learn it more.
Yes, I try to test getJSONObj-method, but those URL & URLConnection is making it difficult for me to understand how to test my method by "faking" it to believe it really takes connection.
Can't realize what you really mean, so here is the current code when I tried to do as you said Jeff Bowman. (Still using that big String, because I tried to get it first done with the current style and then get better performance with Reader's after this is working.)
Discusser.java
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import org.apache.commons.io.IOUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
public class Discusser implements URLOpener {
public JSONObject getJSONObj() throws IOException, ParseException {
String jsonString;
try (InputStream is = openURL("RealUrlStringGoesHere");) {
jsonString = IOUtils.toString(is);
}
return (JSONObject) JSONValue.parseWithException(jsonString);
}
#Override
public InputStream openURL(String urlStr) throws IOException {
URL url = new URL(urlStr);
URLConnection urlConnection = url.openConnection();
return urlConnection.getInputStream();
}
}
URLOpener.java
import java.io.IOException;
import java.io.InputStream;
public interface URLOpener {
InputStream openURL(String urlStr) throws IOException;
}
This test is almost useless to show, because I think it's totally wrong how I try to use the mock. (It's returning null when discusser.getJSONObj())
DiscusserTest.java
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import org.json.simple.JSONObject;
import org.junit.Test;
import org.mockito.Mockito;
public class DiscusserTest {
#Test
public void testGetJSONObj() throws Exception {
JSONObject expectedJSONObject = createExpected();
ByteArrayInputStream inputForMock = new ByteArrayInputStream(generateJSONString().getBytes("UTF-8"));
// Should I mock like this or...
Discusser discusser = Mockito.mock(Discusser.class);
Mockito.when(discusser.openURL("asd")).thenReturn(inputForMock);
//
assertEquals(expectedJSONObject, discusser.getJSONObj());
}
private String generateJSONString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append("\"id\":\"123\",");
sb.append("\"name\":\"test\"");
sb.append("}");
return sb.toString();
}
#SuppressWarnings("unchecked")
private JSONObject createExpected() {
JSONObject obj = new JSONObject();
obj.put("id", 123);
obj.put("name", "test");
return obj;
}
}
Could you or someone else give guidance / example how getJSONObj()-method in Discusser should be tested?
You could start a server within your test and test against this server. You can use MockServer for this.
I managed to get it working and here are the results. If you have improving ideas or other suggestions, I'm very pleased to have them.
I added setter for URLOpener in Discusser, so then I can put that mocked one there quite easily.
Discusser.java
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
public class Discusser implements URLOpener {
private URLOpener urlOpener;
public JSONObject getJSONObj() throws IOException, ParseException {
JSONObject jsonObj;
try (InputStream is = openURL("RealUrlStringGoesHere");) {
jsonObj = (JSONObject) JSONValue.parse(new InputStreamReader(is));
}
return jsonObj;
}
#Override
public InputStream openURL(String urlStr) throws IOException {
return urlOpener.openURL(urlStr);
}
public void setURLOpener(URLOpener urlOpener) {
this.urlOpener = urlOpener;
}
}
DiscusserTest.java
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.json.simple.JSONObject;
import org.junit.Test;
public class DiscusserTest {
#Test
public void testGetJSONObj() throws Exception {
Discusser discusser = new Discusser();
discusser.setURLOpener(createMockURLOpener());
assertEquals(createExpected(), discusser.getJSONObj());
}
private URLOpener createMockURLOpener() throws IOException {
URLOpener mockUrlOpener = mock(URLOpener.class);
ByteArrayInputStream input = new ByteArrayInputStream(generateJSONString().getBytes("UTF-8"));
when(mockUrlOpener.openURL("RealUrlStringGoesHere")).thenReturn(input);
return mockUrlOpener;
}
private String generateJSONString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append("\"id\":\"123\",");
sb.append("\"name\":\"test\"");
sb.append("}");
return sb.toString();
}
#SuppressWarnings("unchecked")
private JSONObject createExpected() {
JSONObject obj = new JSONObject();
obj.put("id", "123");
obj.put("name", "test");
return obj;
}
}
URLOpener.java
import java.io.IOException;
import java.io.InputStream;
public interface URLOpener {
InputStream openURL(String urlStr) throws IOException;
}
What are you trying to test?
If you're trying to test that interactions happen correctly with the real server, then no amount of mocking will help you. You'd want to write an integration test.
If you're trying to test that interactions happen through Java's URLConnection, then a mock server might work the way Stefan Birkner describes. I don't think that's necessarily a useful thing, though, as the Java URLConnection framework is exceedingly well-tested third-party code.
It looks like the component that is most testable here is getJSONObj(), where the part that is not so testable is the function that turns a URL into an InputStream. Make that your interface, instead:
interface URLOpener {
InputStream openURL(String url);
}
At that point, you can use a very simple real implementation in your production code, or pass in a dead-simple mock that returns a ByteArrayInputStream.
Side note: You may find you have better performance if you use JSONValue.parse(Reader) instead of trying to construct one big String containing the entire JSON file. This wouldn't interfere with mocking, as you could just use StringReader instead.
/* in prod, within your actual URLOpener */
return new InputStreamReader(urlConnection.getInputStream());
/* in test, for your mock URLOpener */
when(mockUrlOpener.openURL(expectedURL)).thenReturn(new StringReader(testJSON));
JSONValue value = JSONValue.parse(new BufferedReader(readerFromUrl));

Java: Simple HTTP Server application that responds in JSON

I want to create a very simple HTTP server application in Java.
For example, if I run the server on localhost in port 8080, and I make to following call from my browser, I want to get a Json array with the string 'hello world!':
http://localhost:8080/func1?param1=123&param2=456
I would like to have in the server something that looks like this (very abstract code):
// Retunrs JSON String
String func1(String param1, String param2) {
// Do Something with the params
String jsonFormattedResponse = "['hello world!']";
return jsonFormattedResponse;
}
I guess that this function should not actually "return" the json, but to send it using some HTTP response handler or something similar...
What it the simplest way to do it, without a need to get familiar with many kinds of 3rd party libraries that have special features and methodology?
You could use classes from the package com.sun.net.httpserver:
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class JsonServer {
private static final String HOSTNAME = "localhost";
private static final int PORT = 8080;
private static final int BACKLOG = 1;
private static final String HEADER_ALLOW = "Allow";
private static final String HEADER_CONTENT_TYPE = "Content-Type";
private static final Charset CHARSET = StandardCharsets.UTF_8;
private static final int STATUS_OK = 200;
private static final int STATUS_METHOD_NOT_ALLOWED = 405;
private static final int NO_RESPONSE_LENGTH = -1;
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String ALLOWED_METHODS = METHOD_GET + "," + METHOD_OPTIONS;
public static void main(final String... args) throws IOException {
final HttpServer server = HttpServer.create(new InetSocketAddress(HOSTNAME, PORT), BACKLOG);
server.createContext("/func1", he -> {
try {
final Headers headers = he.getResponseHeaders();
final String requestMethod = he.getRequestMethod().toUpperCase();
switch (requestMethod) {
case METHOD_GET:
final Map<String, List<String>> requestParameters = getRequestParameters(he.getRequestURI());
// do something with the request parameters
final String responseBody = "['hello world!']";
headers.set(HEADER_CONTENT_TYPE, String.format("application/json; charset=%s", CHARSET));
final byte[] rawResponseBody = responseBody.getBytes(CHARSET);
he.sendResponseHeaders(STATUS_OK, rawResponseBody.length);
he.getResponseBody().write(rawResponseBody);
break;
case METHOD_OPTIONS:
headers.set(HEADER_ALLOW, ALLOWED_METHODS);
he.sendResponseHeaders(STATUS_OK, NO_RESPONSE_LENGTH);
break;
default:
headers.set(HEADER_ALLOW, ALLOWED_METHODS);
he.sendResponseHeaders(STATUS_METHOD_NOT_ALLOWED, NO_RESPONSE_LENGTH);
break;
}
} finally {
he.close();
}
});
server.start();
}
private static Map<String, List<String>> getRequestParameters(final URI requestUri) {
final Map<String, List<String>> requestParameters = new LinkedHashMap<>();
final String requestQuery = requestUri.getRawQuery();
if (requestQuery != null) {
final String[] rawRequestParameters = requestQuery.split("[&;]", -1);
for (final String rawRequestParameter : rawRequestParameters) {
final String[] requestParameter = rawRequestParameter.split("=", 2);
final String requestParameterName = decodeUrlComponent(requestParameter[0]);
requestParameters.putIfAbsent(requestParameterName, new ArrayList<>());
final String requestParameterValue = requestParameter.length > 1 ? decodeUrlComponent(requestParameter[1]) : null;
requestParameters.get(requestParameterName).add(requestParameterValue);
}
}
return requestParameters;
}
private static String decodeUrlComponent(final String urlComponent) {
try {
return URLDecoder.decode(urlComponent, CHARSET.name());
} catch (final UnsupportedEncodingException ex) {
throw new InternalError(ex);
}
}
}
On a side note, ['hello world!'] is invalid JSON. Strings must be enclosed in double quotes.
You could :
Install Apache Tomcat, and just drop a JSP into the ROOT project that implements this.
I second #xehpuk. It's not actually that hard to write your own single class HTTP server using just standard Java. If you want to do it in earlier versions you can use NanoHTTPD, which is a pretty well known single class HTTP server implementation.
I would personally recommend that you look into Apache Sling (pretty much THE Reference implementation of a Java REST api). You could probably implement your requirements here using Sling without ANY programming at all.
But as others have suggested, the standard way to do this is to create a java WAR and deploy it into a 'servlet container' such as Tomcat or Jetty etc.
If you are already familiar with servlet you do not need much to create a simple server to achieve what you want. But I would like to emphasize that your needs will likely to increase rapidly and therefore you may need to move to a RESTful framework (e.g.: Spring WS, Apache CXF) down the road.
You need to register URIs and get parameters using the standard servlet technology. Maybe you can start here: http://docs.oracle.com/cd/E13222_01/wls/docs92/webapp/configureservlet.html
Next, you need a JSON provider and serialize (aka marshall) it in JSON format. I recommend JACKSON. Take a look at this tutorial:
http://www.sivalabs.in/2011/03/json-processing-using-jackson-java-json.html
Finally, your code will look similar to this:
public class Func1Servlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String p1 = req.getParameter("param1");
String p2 = req.getParameter("param2");
// Do Something with the params
ResponseJSON resultJSON = new ResponseJSON();
resultJSON.setProperty1(yourPropert1);
resultJSON.setProperty2(yourPropert2);
// Convert your JSON object into JSON string
Writer strWriter = new StringWriter();
mapper.writeValue(strWriter, resultJSON);
String resultString = strWriter.toString();
resp.setContentType("application/json");
out.println(resultString );
}
}
Map URLs in your web.xml:
<servlet>
<servlet-name>func1Servlet</servlet-name>
<servlet-class>myservlets.func1servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>func1Servlet</servlet-name>
<url-pattern>/func1/*</url-pattern>
</servlet-mapping>
Keep in mind this is a pseudo-code. There are lots you can do to enhance it, adding some utility classes, etc...
Nevertheless, as your project grows your need for a more comprehensive framework becomes more evident.
Run main to start the server on port 8080
public class Main {
public static void main(String[] args) throws LifecycleException {
Tomcat tomcat = new Tomcat();
Context context = tomcat.addContext("", null);
Tomcat.addServlet(context, "func1", new HttpServlet() {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Object response = func1(req.getParameter("param1"), req.getParameter("param2"));
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(resp.getWriter(), response);
}
});
context.addServletMappingDecoded("/func1", "func1");
tomcat.start();
tomcat.getServer().await();
}
private static String[] func1(String p1, String p2) {
return new String[] { "hello world", p1, p2 };
}
}
Gradle dependencies:
dependencies {
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.28' // doesn't work with tomcat 9
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.4'
}

Testing outputstream.write(<String>) without creating a file

I am testing a output stream in java something like below.
Writer outputStream = getOutputStream(fileName);
if(outputStream != null) {
try {
outputStream.write(inputText);
}
finally {
outputStream.close();
}
}
else {
throw new IOException("Output stream is null");
}
I am write a mockito test as below
public void testFileWrite() throws IOException {
when(testObj.getOutputStream(outputFileNameValidValue)).thenReturn(outputStreamMock);
doNothing().when(outputStreamMock).write(Matchers.anyString());
doNothing().when(bufferedReaderMock).close();
testObj.write(outputFileNameValidValue, reveredFileInput);
verify(outputStreamMock).write(Matchers.anyString());
verify(outputStreamMock).close();
}
The problem is when you create OutputStreamWriter(new FileOutputStream(filename)) a physical file on the disk is created.
Can we test Outputstream.write without actually writing a file on the disk?
Thanks
Anand
You can use ByteArrayOutputStream which writes the data in memory. You can read this with a ByteArrayInputStream.
An alternative is to write an expecting OutputStream which fails as soon as you attempt to write an incorrect byte. This can be helpful to see exactly where/why a test fails.
You could try using System.out for your output which is actually a Printstream, which is a subclass of OutputStream
see:
http://docs.oracle.com/javase/6/docs/api/java/lang/System.html
http://docs.oracle.com/javase/6/docs/api/java/io/PrintStream.html
As other suggested already you need to be able to inject a mocked OutputStream in your class under test. As your class under test needs a OutputStream which writes into a given file, you will need to inject a mockable OutputStreamFactory into your class under test.
I have this code for you which is fully self contained:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.IOException;
import java.io.OutputStream;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class Test9328173 {
private ClassUnderTest testObj;
#Mock
private OutputStreamFactory factory;
#Mock
private OutputStream stream;
#Before
public void setUp() throws Exception {
testObj = new ClassUnderTest();
testObj.factory = factory;
}
#Test
public void testFileWrite() throws Exception {
when(factory.create("filename")).thenReturn(stream);
testObj.write("filename", new byte[]{1, 2, 3});
verify(stream).write(new byte[]{1, 2, 3});
verify(stream).close();
}
private class ClassUnderTest {
private OutputStreamFactory factory;
public void write(String filename, byte[] content) throws IOException {
OutputStream stream = factory.create(filename);
try {
stream.write(content);
} finally {
stream.close();
}
}
}
private interface OutputStreamFactory {
OutputStream create(String filename);
}
}
You should mock up your getOutputStream: is should return mocked output stream object. Invocation of new FileOutputStream indeed creates file on disk.
Theoretically you can mock up file system itself but it is much more complicated.
And BTW if(outputStream != null) is redundant: stream can never be null. If it cannot be created the method should throw exception. It is not C, it is Java. :)
You should have the mocked getOutputStream(String) return a java.io.StringWriter and you can then assert that the expected content was written.
public void testFileWrite() throws IOException {
StringWriter writer = new StringWriter();
when(testObj.getOutputStream(outputFileNameValidValue)).thenReturn(writer);
testObj.write(outputFileNameValidValue, reveredFileInput);
assertEquals(reveredFileInput, writer.toString());
verify(writer).close();
}

Redirect console output to string in Java

I have one method whose return type is void and it prints directly on console.
However I need that output in a String so that I can work on it.
As I can't make any changes to the method with return type void I have to redirect that output to a String.
How can I redirect it in Java?
If the function is printing to System.out, you can capture that output by using the System.setOut method to change System.out to go to a PrintStream provided by you. If you create a PrintStream connected to a ByteArrayOutputStream, then you can capture the output as a String.
Example:
// Create a stream to hold the output
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
// IMPORTANT: Save the old System.out!
PrintStream old = System.out;
// Tell Java to use your special stream
System.setOut(ps);
// Print some output: goes to your special stream
System.out.println("Foofoofoo!");
// Put things back
System.out.flush();
System.setOut(old);
// Show what happened
System.out.println("Here: " + baos.toString());
This program prints just one line:
Here: Foofoofoo!
Here is a utility Class named ConsoleOutputCapturer. It allows the output to go to the existing console however behind the scene keeps capturing the output text. You can control what to capture with the start/stop methods. In other words call start to start capturing the console output and once you are done capturing you can call the stop method which returns a String value holding the console output for the time window between start-stop calls. This class is not thread-safe though.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;
public class ConsoleOutputCapturer {
private ByteArrayOutputStream baos;
private PrintStream previous;
private boolean capturing;
public void start() {
if (capturing) {
return;
}
capturing = true;
previous = System.out;
baos = new ByteArrayOutputStream();
OutputStream outputStreamCombiner =
new OutputStreamCombiner(Arrays.asList(previous, baos));
PrintStream custom = new PrintStream(outputStreamCombiner);
System.setOut(custom);
}
public String stop() {
if (!capturing) {
return "";
}
System.setOut(previous);
String capturedValue = baos.toString();
baos = null;
previous = null;
capturing = false;
return capturedValue;
}
private static class OutputStreamCombiner extends OutputStream {
private List<OutputStream> outputStreams;
public OutputStreamCombiner(List<OutputStream> outputStreams) {
this.outputStreams = outputStreams;
}
public void write(int b) throws IOException {
for (OutputStream os : outputStreams) {
os.write(b);
}
}
public void flush() throws IOException {
for (OutputStream os : outputStreams) {
os.flush();
}
}
public void close() throws IOException {
for (OutputStream os : outputStreams) {
os.close();
}
}
}
}
Although this question is very old and has already very good answers I want to provide an alternative. I created a library specifically for this use case. It is called Console Captor and you can add it with the following snippet:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>consolecaptor</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
Example class
public class FooService {
public void sayHello() {
System.out.println("Keyboard not responding. Press any key to continue...");
System.err.println("Congratulations, you are pregnant!");
}
}
Unit test
import static org.assertj.core.api.Assertions.assertThat;
import nl.altindag.console.ConsoleCaptor;
import org.junit.jupiter.api.Test;
public class FooServiceTest {
#Test
public void captureStandardAndErrorOutput() {
ConsoleCaptor consoleCaptor = new ConsoleCaptor();
FooService fooService = new FooService();
fooService.sayHello();
assertThat(consoleCaptor.getStandardOutput()).contains("Keyboard not responding. Press any key to continue...");
assertThat(consoleCaptor.getErrorOutput()).contains("Congratulations, you are pregnant!");
consoleCaptor.close();
}
}
If you are using Spring Framework, there is a really easy way to do this with OutputCaptureExtension:
#ExtendWith(OutputCaptureExtension.class)
class MyTest {
#Test
void test(CapturedOutput output) {
System.out.println("ok");
assertThat(output).contains("ok");
System.err.println("error");
}
#AfterEach
void after(CapturedOutput output) {
assertThat(output.getOut()).contains("ok");
assertThat(output.getErr()).contains("error");
}
}

Categories

Resources