Using Mockito and Junit5 get past a try statement with InputStream? - java

I'm trying to test code which is inside a try-catch statement such as:
#Service
public class DoSomething {
public void uploadFile(String filename) {
try{
File file = new File(filename + ".txt");
ByteArrayInputStream byteInputStream = new ByteArrayInputStream(FileUtils.readFileToByteArray(file));
// ...other code
method2(filename)
// ... more code
} catch(...) {...}
}
public void method2(filename){...do something here}
}
I want to test what's inside of the try statement however I'm unable to mock the InputStream so it just goes straight to the catch statement when I try to Spy on uploadFile. How can I mock the InputStream so it returns a file I have in the temp folder or just not go to the catch statement such that I may test the rest of my code. i.e.) I'm trying to verify method2 will be within the uploadFile method.

Related

JUnit Mockito URL and HttpsURLConnection

I have the following code:
public byte[] myFunction(String s1, String s2, String s3) {
try {
URL myUrl = new URL("https://myUrl");
HttpsURLConnection connection = (HttpsURLConnection) myUrl.openConnection();
connection.connect();
int responseCode = connection.getResponseCode();
String lastPacket = connection.getHeaderField("LastPacket");
byte fileContent[] = IOUtils.toByteArray((InputStream) connection.getInputStream());
return fileContent;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I want to unit test the above code.
So I've written JUnit using MockitoJUnitRunner as:
#RunWith(MockitoJUnitRunner.class)
public class EvmlSeviceImplTest {
#Mock
private URL url;
#Mock
private HttpsURLConnection httpsURLConnection;
#InjectMocks
private MyClass myClass;
#Before
public void preSetup() {
try {
Mockito.doReturn(httpsURLConnection).when(url).openConnection();
Mockito.doReturn(200).when(httpsURLConnection).getResponseCode();
Mockito.doReturn("-1").when(httpsURLConnection).getHeaderField("LastPacket");
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testMyFunction() {
myClass.myFunction("my", "dummy", "data");
}
}
But the problem here is that URL is final class and Mockito cannot spy final classes. Since we're using MockitoJUnitRunner, how can this be done?
I'm getting the exception:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
java.net.URL
Let's look closer at your function and what you try to test:
URL myUrl = new URL("https://myUrl"); // (1)
HttpsURLConnection connection = (HttpsURLConnection) myUrl.openConnection(); // (2)
connection.connect(); // (3)
int responseCode = connection.getResponseCode(); // (4)
String lastPacket = connection.getHeaderField("LastPacket");
byte fileContent[] = IOUtils.toByteArray((InputStream) connection.getInputStream()); // (5)
return fileContent;
So in line 1, 2 and 3 you create a connection object to some remote resource and connect to it. You use SDK's classes for that.
Then you check the response code (4) and read from the stream (5). Again, you are using SDK's standard library for that.
Question is: do you really want to unit test code that we are safe to assume is working?
What I would do is extract lines from 1 to 4 to a separate class with a method that returns a stream that you can read. And then, in your test mock that stream according to your needs. But don't unit test such low-level code.
Looks to me like your preSetup is wrong as well.
Even though you mock HttpsURLConnection (httpsURLConnection) and URL (url), this isn't used in the test.
You can't just use "url" (the mocked URL) in your test or setup, since in the method you want to test (myFunction), a new URL (myUrl) is created. This "myUrl" is not the same as "url" you mocked.
I would pass an URLConnection as an argument to that method (or create a second function). You then have full control over it and can mock it.
// Edit:
Mocking a URL in Java

java - opencsv class works perfect individually but doesn't write CSVs when called by another class

I'm using html form to take input of a file and uploading it using doPost() method of HTTPservlet. [This part is done in package one] Now function from class Main.java to parse the uploaded file is called in this doPost method. This function resides in the other package. [Moving to the other package-> ] Here the class Main.java calls Integrated.java which has all the instances of class and functions needed to be called in order to parse the file that was uploaded. (I have separate classes for extracting data, chunking, sorting and generating csv out of the data parsed)
After I parse the file, I put values in variables which are initialized in the last file CSVgenerator.java. Values to these variables are added before in the class SortAndOutput.java. To test that I'm getting the right values in my class, I print the variables right before I call the functions writingDatabase() and writingTraining(). These two functions save the values of the variables in CSV files.
I'm using OpenCSV for writing the files. Using apache Tika for extraction of data and lingpipe to parse.
THE BIG PROBLEM:
I (intentionally) write main func in CSVgenerator.java and test it. Both the files are written properly. The missing value columns are left blank. PERFECT! (If i have any of the CSV open separately, it raises an exception like it should)
But when i comment out the main func and run the whole project together on server, the CSVs are not written. Even if the files are opened separately(outside eclipse), it doesn't give an error.
I have tested the whole integrated Java code by putting a main method in Main.java and it runs perfectly. The problem occurs when servlet is run/I run project on server.
I don't know if other files will be helpful or not, I'm posting the hierarchy and CSVgenerator class
Workspace Screenshot
CSVGenerator.java
package com.fypv1.parser;
import java.io.*;
import java.util.*;
import com.opencsv.*;
public class CSVgenerator {
public static String CGPAinitial="-";
public static String universityNameOut="-";
public static String emailIDOut="-";
public static String phoneNoOut="-";
public static String phpKnow="NO";
public static String databaseKnow="NO";
public static String jsKnow="NO";
public static String bootStrapKnow="NO";
public static String aspKnow="NO";
public static String htmlKnow="NO";
public static String cssKnow="NO";
public static String jqueryKnow="NO";
public static String jspKnow="NO";
public static String reactjsKnow="NO";
public static String ajaxKnow="NO";
public static String oopKnow="NO";
public static String javaKnow="NO";
public static String androidKnow="NO";
private int idNumber;
Writer wr;
Scanner scanner;
/*
* public static void main(String[] args) throws IOException {
new CSVgenerator();
}
*/
//DEFAULT CLASS CONSTRUCTOR
CSVgenerator() throws IOException
{
String trainingCSV = "TrainingData.csv";
String databaseCSV = "databaseInfo.csv";
String idNumberFile = "idnum.txt";
try {
scanner = new Scanner(new File(idNumberFile));
SortAndOutput.applicantIDnumber=scanner.nextInt();
idNumber=SortAndOutput.applicantIDnumber;
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
System.out.println("IDnumberFileUnavailable");
}
try {
wr = new FileWriter(idNumberFile);
int newIDNumber=idNumber+1;
wr.write(newIDNumber+"");
wr.close();
} catch (IOException e1) {
//
e1.printStackTrace();
}
//System.out.println( String.valueOf(idNumber)+"+"+universityNameOut+"+"+CGPAinitial+"+"+emailIDOut+"+"+ phoneNoOut+"+"+CGPAinitial+"+"+phpKnow+"+"+ databaseKnow+"+"+ jsKnow+"+"+ bootStrapKnow+"+"+ aspKnow+"+"+ htmlKnow+"+"+ cssKnow+"+"+ jqueryKnow+"+"+ jspKnow+"+"+ reactjsKnow+"+"+ ajaxKnow+"+"+ "?");
writingDatabase(databaseCSV);
writingTraining(trainingCSV);
}//CONSTRUCTOR ENDS
void writingDatabase(String databaseCSV) throws IOException {
CSVWriter dbWriter;
String [] record1={String.valueOf(idNumber),universityNameOut,CGPAinitial,emailIDOut, phoneNoOut };
dbWriter= new CSVWriter(new FileWriter(databaseCSV, true));
dbWriter.writeNext(record1);
dbWriter.close();
}
void writingTraining(String trainingCSV) throws IOException {
CSVWriter trainingWriter;
String [] record={String.valueOf(idNumber),CGPAinitial,phpKnow, databaseKnow, jsKnow, bootStrapKnow, aspKnow, htmlKnow, cssKnow, jqueryKnow, jspKnow, reactjsKnow, ajaxKnow, "?"};
trainingWriter = new CSVWriter(new FileWriter(trainingCSV, true));
trainingWriter.writeNext(record);
trainingWriter.close();
}
}
Integrated.java
ResumeUploadService.java (the servlet)
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
try {
Part file = request.getPart("file");
resumeFileName = Paths.get(file.getSubmittedFileName()).getFileName().toString();
file.write(path + resumeFileName);
printOnClient(response, "Upload Successful!");
}
catch (Exception e) {
printOnClient(response, "Upload Failed!");
}
new Main(path,resumeFileName);
}
Please let me know if more code is needed to debug this.
It was updating files both times. When it ran on server, it created files in eclipse folder and when i ran as java application, it updated files in project folder
Your files are written in the application folder unless otherwise specified. This is the relative path to the running application.
Just because you tested the application in Eclipse doesn't mean that the server deployment would write to the same folder.
Ideally, you can specify a configuration parameter to your Servlet for an absolute path where files are to be stored

Getting java.lang.RuntimeException: java.lang.NullPointerException

public class TestScenario1 {
#Test (dataProvider = "test")
public void execute(String TestCol1,String TestCol2, String TestCol3,String TestCol4) throws Exception {
homePage hp = new homePage();
hp.perform(TestCol1, TestCol2, TestCol3, TestCol4);
}
#DataProvider(name= "test")
public Object[][] testcase(ITestContext context) {
String filepath = executionBase.CONFIG.getProperty("filepath");
// String filepath = "D://workspace//Project//data//testData.xlsx";
String sheetname = "Suite";
return executionBase.getTestData(filepath, sheetname);
}
}
If I execute above code it gives me following error:
SKIPPED: execute
java.lang.RuntimeException: java.lang.NullPointerException
at org.testng.internal.MethodInvocationHelper.invokeDataProvider(MethodInvocationHelper.java:162)
at org.testng.internal.Parameters.handleParameters(Parameters.java:430)
But if I comment this line
String filepath =executionBase.CONFIG.getProperty("filepath");
and execute following instead,
String filepath = "D://workspace//Project//data//testData.xlsx";
it works fine
executionBase.class referred constructor code looks like,
public executionBase() throws IOException, FileNotFoundException {
CONFIG = new Properties();
FileInputStream ip = new FileInputStream(System.getProperty("user.dir")+ "//config//config.properties");
CONFIG.load(ip);
}
config.properties file entry goes like this,
filepath=D:\workspace\Project\data\testData.xlsx
Function of executionBase.class works fine for other path variables provided in config.properties, but not sure why I am getting Null value for same under
#DataProvider(name= "test") annotation
Properties.load() will escape back slashes, Try using double backslashes in the config file, or changing to forward slash:
filepath=D:/workspace/Project/data/testData.xlsx
You can also try:
String content = IOUtils.toString(ip, Charset.defaultCharset());
content = content.replaceAll("\\","\\\\");
CONFIG.load(content);
Edit: From what I can see, seems like your executionBase isn't initialized when you run your TestScenario class. Check how is executionBase() is called, and verify its called before calling it in DataProvider.
I faced the exact same problem, Open you test data sheet and press "CTRL+END", if its not the cell(lastrow,lastcolumn), then manually right click on that row or column and DELETE it. Save the file and run it, worked for me !

How to take screenshot of failed test in selenium webdriver using ReportNG

If anyone know how to take screenshot of failed test/methods in selenium webdriver using reportNG then please help.
If you provide code then its helpfull or provide some ideas to solve this problem.
Thanks in advance.
Yes, It is possible.
WebDriver firfoxDriver = new FirefoxDriver();
firfoxDriver.get("https://stackoverflow.com/");
File file = ((TakesScreenshot)firfoxDriver).getScreenshotAs(OutputType.FILE);
// Now you can do whatever you need to do with it, for example copy somewhere
FileUtils.copyFile(file, new File("E:\\Data\\ss.png"));
Or You can use this:
#AfterMethod(alwaysRun=true)
public void catchExceptions(ITestResult result){
Calendar calendar = Calendar.getInstance();
SimpleDateFormat formater = new SimpleDateFormat("dd_MM_yyyy_hh_mm_ss");
String methodName = result.getName();
if(!result.isSuccess()){
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(scrFile, new File((String) PathConverter.convert("failure_screenshots/"+methodName+"_"+formater.format(calendar.getTime())+".png")));
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
Well It's depend on your framework that where the control is going after failing the method.
Suppose in your case it is going in catch exception block then copy that code in catch block.
File file = ((TakesScreenshot)firfoxDriver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(file, new File("YOUR PATH"+Filename+.JPG));
You can use Itestlistener in testng to take screenshot. testng has an in built function by which you can take screenshot when it fails.
Just create a seperate listerner class and paste this code:
public class listeners implements ITestListener{
//This "Base " is the main class where you can pass the result of test case and write the //code for screenshot
//I cam creating an object so I can access the method that I created for screenshot in this base class.
//I am also getting the result name so I can identify which test case it got failed
Base b = new Base();
public void onTestFailure(ITestResult result) {
try {
//Getting the result name by result.getName() method
b.getScreenshot( result.getName());
System.out.println("The Failed test is=="+result.getName());
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I created this getScreenshot method in the base class and passing the result name
public class Base {
public WebDriver InitializeDr() throws IOException {
//Creating the method to take screenshot in the Base class
public void getScreenshot(String result) throws IOException
{
File dest=new File("D:\\Work\\KT\\Scr Shot\\"+result+"test.png");
File src=((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(src, dest);
}
}
}

Mocking/Testing HTTP Get Request

I'm trying to write unit tests for my program and use mock data. I'm a little confused on how to intercept an HTTP Get request to a URL.
My program calls a URL to our API and it is returned a simple XML file. I would like the test to instead of getting the XML file from the API online to receive a predetermined XML file from me so that I can compare the output to the expected output and determine if everything is working correctly.
I was pointed to Mockito and have been seeing many different examples such as this SO post, How to use mockito for testing a REST service? but it's not becoming clear to me how to set it all up and how to mock the data (i.e., return my own xml file whenever the call to the URL is made).
The only thing I can think of is having another program made that's running locally on Tomcat and in my test pass a special URL that calls the locally running program on Tomcat and then return the xml file that I want to test with. But that just seems like overkill and I don't think that would be acceptable. Could someone please point me in the right direction.
private static InputStream getContent(String uri) {
HttpURLConnection connection = null;
try {
URL url = new URL(uri);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");
return connection.getInputStream();
} catch (MalformedURLException e) {
LOGGER.error("internal error", e);
} catch (IOException e) {
LOGGER.error("internal error", e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
return null;
}
I am using Spring Boot and other parts of the Spring Framework if that helps.
Part of the problem is that you're not breaking things down into interfaces. You need to wrap getContent into an interface and provide a concrete class implementing the interface. This concrete class will then
need to be passed into any class that uses the original getContent. (This is essentially dependency inversion.) Your code will end up looking something like this.
public interface IUrlStreamSource {
InputStream getContent(String uri)
}
public class SimpleUrlStreamSource implements IUrlStreamSource {
protected final Logger LOGGER;
public SimpleUrlStreamSource(Logger LOGGER) {
this.LOGGER = LOGGER;
}
// pulled out to allow test classes to provide
// a version that returns mock objects
protected URL stringToUrl(String uri) throws MalformedURLException {
return new URL(uri);
}
public InputStream getContent(String uri) {
HttpURLConnection connection = null;
try {
Url url = stringToUrl(uri);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");
return connection.getInputStream();
} catch (MalformedURLException e) {
LOGGER.error("internal error", e);
} catch (IOException e) {
LOGGER.error("internal error", e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
return null;
}
}
Now code that was using the static getContent should go through a IUrlStreamSource instances getContent(). You then provide to the object that you want to test a mocked IUrlStreamSource rather than a SimpleUrlStreamSource.
If you want to test SimpleUrlStreamSource (but there's not much to test), then you can create a derived class that provides an implementation of stringToUrl that returns a mock (or throws an exception).
The other answers in here advise you to refactor your code to using a sort of provider which you can replace during your tests - which is the better approach.
If that isn't a possibility for whatever reason you can install a custom URLStreamHandlerFactory that intercepts the URLs you want to "mock" and falls back to the standard implementation for URLs that shouldn't be intercepted.
Note that this is irreversible, so you can't remove the InterceptingUrlStreamHandlerFactory once it's installed - the only way to get rid of it is to restart the JVM. You could implement a flag in it to disable it and return null for all lookups - which would produce the same results.
URLInterceptionDemo.java:
public class URLInterceptionDemo {
private static final String INTERCEPT_HOST = "dummy-host.com";
public static void main(String[] args) throws IOException {
// Install our own stream handler factory
URL.setURLStreamHandlerFactory(new InterceptingUrlStreamHandlerFactory());
// Fetch an intercepted URL
printUrlContents(new URL("http://dummy-host.com/message.txt"));
// Fetch another URL that shouldn't be intercepted
printUrlContents(new URL("http://httpbin.org/user-agent"));
}
private static void printUrlContents(URL url) throws IOException {
try(InputStream stream = url.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
String line;
while((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
private static class InterceptingUrlStreamHandlerFactory implements URLStreamHandlerFactory {
#Override
public URLStreamHandler createURLStreamHandler(final String protocol) {
if("http".equalsIgnoreCase(protocol)) {
// Intercept HTTP requests
return new InterceptingHttpUrlStreamHandler();
}
return null;
}
}
private static class InterceptingHttpUrlStreamHandler extends URLStreamHandler {
#Override
protected URLConnection openConnection(final URL u) throws IOException {
if(INTERCEPT_HOST.equals(u.getHost())) {
// This URL should be intercepted, return the file from the classpath
return URLInterceptionDemo.class.getResource(u.getHost() + "/" + u.getPath()).openConnection();
}
// Fall back to the default handler, by passing the default handler here we won't end up
// in the factory again - which would trigger infinite recursion
return new URL(null, u.toString(), new sun.net.www.protocol.http.Handler()).openConnection();
}
}
}
dummy-host.com/message.txt:
Hello World!
When run, this app will output:
Hello World!
{
"user-agent": "Java/1.8.0_45"
}
It's pretty easy to change the criteria of how you decide which URLs to intercept and what you return instead.
The answer depends on what you are testing.
If you need to test the processing of the InputStream
If getContent() is called by some code that processes the data returned by the InputStream, and you want to test how the processing code handles specific sets of input, then you need to create a seam to enable testing. I would simply move getContent() into a new class, and inject that class into the class that does the processing:
public interface ContentSource {
InputStream getContent(String uri);
}
You could create a HttpContentSource that uses URL.openConnection() (or, better yet, the Apache HttpClientcode).
Then you would inject the ContentSource into the processor:
public class Processor {
private final ContentSource contentSource;
#Inject
public Processor(ContentSource contentSource) {
this.contentSource = contentSource;
}
...
}
The code in Processor could be tested with a mock ContentSource.
If you need to test the fetching of the content
If you want to make sure that getContent() works, you could create a test that starts a lightweight in-memory HTTP server that serves the expected content, and have getContent() talk to that server. That does seem overkill.
If you need to test a large subset of the system with fake data
If you want to make sure things work end to end, write an end to end system test. Since you indicated you use Spring, you can use Spring to wire together parts of the system (or to wire the entire system, but with different properties). You have two choices
Have the system test start a local HTTP server, and when you have your test create your system, configure it to talk to that server. See the answers to this question for ways to start the HTTP server.
Configure spring to use a fake implementation of ContentSource. This gets you slightly less confidence that everything works end-to-end, but it will be faster and less flaky.

Categories