Can you mock out method calls in the class you're testing? - java

I'm trying to write JUnit tests for my code but with in some of my methods other methods are called. Is it possible to mock these calls out?
E.g.
s3FileWrite(File file, Status status)
{
S3 s3 = new S3(file.getName, s3Service)
String key = s3.getKey();
String bucket = s3.getBucket();
File tmp = new File("tmp/" + s3.getName());
writeFile(key, bucket, tmp, status); //local method call I want to mock out
}//awsFileWrite
The writeFile method is what I want to mock out and it's part of the class I am testing, but I don't know how to mock it out. I thought mocking out the class I'm testing and then adding the call to my expectations would do it but it still calls the method.
Can anyone give me some advice on what to do here please?
EDIT:
My JMock code looks like this:
#Test
public void testS3FileWrite()
{
fileName = context.mock(File.class);
s3Service = context.mock(FileDataAccessor.class);
s3 = context.mock(S3.class);
reportWriter = context.mock(ReportWriter.class);
try
{
context.checking(new Expectations(){{
oneOf(fileMetaData).getKey();
will(returnValue("s3Key"));
oneOf(fileMetaData).getBucketName();
will(returnValue("BucketName"));
oneOf(fileMetaData).getName();
will(returnValue("TempFile"));
((MethodClause) oneOf (any(File.class))).method("File").with(same("tmp/TempFile"));
oneOf(reportWriter).writeFile(with(same("s3Key")),
with(same("BucketName")),
with(any(File.class),
with(same(Status.OK)));
}});//Expectations
}
catch (Exception e)
{
ErrorStatus.debug("Exception in ReportTest.testS3FileWrite: " + e);
}//try-catch
ReportWriter test = new ReportWriter(status);
test.awsFileWrite(fileName, Status.OK);
}//testAWSFileWrite

PowerMock lets you partially mock classes, but it's designed for EasyMock not JMock. In any case, this is not the best approach.
Add a new class FileWriter and move the writeFile method to it, then in your class under test,
delegate to one:
// default implementation, can be replaced in tests
FileWriter fileWriter = new FileWriter();
writeFile(key, bucket, tmp, status) {
fileWriter.write(key, bucket, tmp, status);
};
In your test code, overwrite the fileWriter field in a the class under test (add a setter or make the field protected) with a mock FileWriter.

Related

Mockito mocking a new instance call with parameters

For school purposes I am creating an application that's working with a stock API.
I am trying to write a test for a method that gets all the stock data of the last 10 years. Instead of actually getting all that data, I want to throw an exception.
The method I Want to test:
#Override
public List<StockData> getAllTeslaStockData() throws AlphaVantageException {
List<StockData> stockData;
AlphaVantageConnector apiConnector = new AlphaVantageConnector(APIKEY, TIMEOUT);
TimeSeries stockTimeSeries = new TimeSeries(apiConnector);
try {
Daily responseDaily = stockTimeSeries.daily("TSLA", OutputSize.FULL);
stockData = responseDaily.getStockData();
} catch (AlphaVantageException e) {
LOGGER.log(Level.SEVERE, "something went wrong: ", e);
throw e;
}
return stockData;
}
The stockTimeSeries.daily(....) call can throw the AlphaVantageException.
I've mocked the TimeSeries class like this:
TimeSeries stockTimeSeries = mock(TimeSeries.class);
In my test class I want to mock this call, and return an exception instead of actual data.
when(stockTimeSeries.daily("TSLA", OutputSize.FULL)).thenThrow(new AlphaVantageException("No stock data available"));
Regardless of how I am trying to mock this bit of code, it'll never throw the exception. It will always just execute the code, and return valid stock data, instead of throwing the exception like i've tried to do.
How can I mock this bit of code, so that itll throw the exception I am expecting for my tests.
The AlphaVantageConnector, TimeSeries and Daily classes are part of a library used to access the stock API, so I can't change these classes.
I am using JUnit 4.12 and Mockito to try and achieve this.
You can use thenThrow() method. Below is the example
#Test(expected = NullPointerException.class)
public void whenConfigNonVoidRetunMethodToThrowEx_thenExIsThrown() {
MyDictionary dictMock = mock(MyDictionary.class);
when(dictMock.getMeaning(anyString()))
.thenThrow(NullPointerException.class);
dictMock.getMeaning("word");
The TimeSeries object is created in the method itself, so you can't mock it - mocking is intended to mock members.
What you can do is to do something like
class YourClass {
private Supplier<TimeSeries> seriesCreator = () -> {
return new TimeSeries(new AlphaVantageConnector(APIKEY, TIMEOUT));
}
which you use to create the series in your method
#Override
public List<StockData> getAllTeslaStockData() throws AlphaVantageException {
TimeSeries stockTimeSeries = seriesCreator.get();
Now you can mock that Supplier.
#Mock Supplier<TimeSeries> seriesCreatorMock;
#InjectMocks MyClass sut;
and in your test
#Test(expected = AlphaVantageException.class)
void testException() {
when(seriesCreatorMock.get()).thenThrow(new AlphaVantageException());
sut.getAllTeslaStockData()
}
EDIT: as suggested by Angkur in the comments, the clean way would be to
class SeriesCreator implements Supplier<TimeSeries> {
public TimeSeries get() {
return new TimeSeries(new AlphaVantageConnector(APIKEY, TIMEOUT));
}
}
class YourClass {
private Supplier<TimeSeries> seriesCreator = new SeriesCreator();
// ...
The code in the main class is creating a new instance of TimeSeries which it will use every time this method is called, so the mocked TimeSeries object is not getting used at all.
TimeSeries stockTimeSeries = new TimeSeries(apiConnector); // --> This is not getting mocked
try {
Daily responseDaily = stockTimeSeries.daily("TSLA", OutputSize.FULL);
stockData = responseDaily.getStockData();
}
You should create another method in your class (or even a separate class if it better satisfies the SOLID principles) which returns you the TimeSeries object. Something like:-
<access modifier> TimeSeries getTimeSeries(...) {
}
and then this method should be mocked in the Junit, and when mocked, it should return the Mocked TimeSeries reference (which is created in TimeSeries stockTimeSeries = mock(TimeSeries.class); ). You would need to use .spy() on the main class (unless you are using a different class to create TimeSeries object) in order to be able to mock the specific method getTimeSeries() but not the others.
MainClass mainObject = Mockito.spy(new MainClass());
Mockito.when(mainObject.getTimeSeries()).thenReturn(stockTimeSeries);
Then, the method call stockTimeSeries.daily() will get actually mocked by your existing code :
when(stockTimeSeries.daily("TSLA", OutputSize.FULL)).thenThrow(new AlphaVantageException("No stock data available"));
NOTE: you should also consider using .anyString() style methods provided by Mockito API while mocking.

Mocking class is returning null instead of data

In my Junit test, I'm doing the following in my Junit test :
#Before
public void setUp() throws Exception {
reportQueryParams = ReportQueryParams.builder()
.id("07")
.build();
}
#Test
public void tabSerializerTest() {
MetricsSerializer mockMonth = mock(MetricsSerializer.class);
when(mockMonth.getCurrentMonth()).thenReturn("July");
String tabSeparated = mockMonth.serializeMetrics(reportQueryParams);
String expected = new StringBuilder().append("074")
.append("\t")
.append("July")
.toString();
assertEquals(expected, tabSeparated);
}
The function which I am testing:
public String serializeMetrics(final ReportQueryParams reportQueryParams) {
stringJoiner = new StringJoiner("\t");
addValueFromString(reportQueryParams.getId());
addValueFromString(getCurrentMonth());
return stringJoiner.toString();
}
public String getCurrentMonth() {
DateFormat monthFormat = new SimpleDateFormat("MMMMM");
return monthFormat.format(new Date());
}
private void addValueFromString(final String value) {
stringJoiner.add(value);
}
My ReportQueryParams class:
public class ReportQueryParams {
private String id;
}
I am getting "null" in the actual data returned and hence the test is failing. How can I fix this?
Don't mock the object you test.What you have written is "create a mock object that returns July for current month". But this mock object doesn't have real behaviour and the other methods return null.
When you test a class you mock the objects required by the class (in order to insulate behaviour) and not the actual class. Here you can create a new MetricsSerializer (by using new :) and call it's method serializeMethod and compare against the current date (instead of July).
The way you have written the class might not be the best testable way though ;)
Your problem is that you are mocking the class, then testing the mock object, rather than testing a "real" object. I can think of two possible solutions.
Use a Mockito Spy instead of a mock. This is like a mock, but it's a real object, and the methods all have their normal behaviour, instead of "no behaviour" by default. You can stub the getCurrentMonth method of your spy, to make it return what you want.
Since the real cause of your problem is the call to new Date(), you could use a time helper, instead of calling new Date() directly in your getCurrentMonth() method. I have described this technique in detail in my answer to this question

How to mock StreamingOutput from jax-rs

I have a class which send video as mp4 file to user (Http request/response)
I want to Mock method with main logic to test it. My code
public StreamingOutput videoAsStream(final String videoUrl) {
try {
final URL url = new URL(videoUrl);
return output -> {
try(final InputStream inputStream = url.openConnection().getInputStream()){
IOUtils.copy(inputStream,output);
output.close();
}
};
} catch (final MalformedURLException e) {
log.error("Url exception for url {}",videoUrl);
throw new UncheckedIOException(e);
}
}
What is my way to mock this logic?
The problem is, that URL is final, so you will have to use at least Mockito 2 to mock it. If you are ready to do that, I see two possibilities:
a) Give the url into the method and not the string, thus allowing you to put a mocked url in there. That would be the most simply method. You could also then create a 2nd convenience method that creates said URL from a string. Those two methods will be easier to test because their scope is smaller.
b) Extract the final URL url = new URL(videoUrl); part into a new class, for example a URL Factory, then mock that to return a mocked URL object in your test.
As soon as you produce stuff with "new" inside your method, this method can become harder to test, because you now cannot separate this test from this object generation.
AFAIK, you can't mock final and static methods/classes using mockito. you would have to depend on PowerMockito. I am currently not able to test your method but if you want to mock final/static, you can do as
first, Add the final/static classes to #PrepareForTest then
InputStream mockInputStream = Mockito.mock(InputStream.class);
OutputStream mockOutputStream = Mockito.mock(OutputStream.class);
PowerMockito.mockStatic(IOUtils.class);
Mockito.when(IOUtils.copy(mockInputStream, mockOutputStream)).thenReturn(1L);
Mockito.doNothing().when(mockOutputStream).close();
Let me know if this does not work for you.

Mocking objects for JUnit test

I'm writing a Junit to test the following method in Client.java:
public FSDataInputStream getObj(String hName, Path p) throws IOException {
String myKey = pathToKey(hName, p);
FileStatus status = memoryCache.getStatus(p.toString());
if (status == null) {
status = getStatus(hName, p, "getObject");
}
if (status.isDirectory()) {
throw new FileNotFoundException("Can't open " + path
+ " because it is a directory");
}
InputStream inputStream = new InputStream(bucket, myKey,
status.getLen(), client, readAhead, inputPolicy);
return new FSDataInputStream(inputStream);
}
Initially I want to test if status == null then getStatus() is invoked and if status.isDirectory(), the FileNotFoundException is thrown
I'm new to Junit so not completely sure what I'm at but to the best of my knowledge I think I need to mock the following:
List item
Client
status
inputStream
possibly memoryCache
So far this is what I've got:
#Before
public final void before() {
private COSAPIClient myClient;
private String myBucket;
FileStatus myStatus;
InputStream myInputStream;
myClient = PowerMockito.mock(AmazonS3.class);
myInputStream = PowerMockito.mock(InputStream.class);
myFileStatus = PowerMockito.mock(FileStatus.class);
}
#Test
public void getObjTest() throws Exception {
URI uri = new URI("xyz://aa-bb-cc/data7-1-23-a.txt");
String hName = "xyz://aa-bb-cc/";
Path p = new Path("cos://aa-bb-cc/data7-1-23-a.txt");
Configuration conf = new Configuration();
myClient = spy(new Client(uri, conf));
myStatus = spy(new FileStatus());
myMemoryCache.getStatus(p.toString());
InputStream = spy(new InputStream(myBucket, objectKey, 300, myClient, 12345678910L, myInputPolicy));
}
It returns a NullPointerError at this line in my program:
FileStatus status = memoryCache.getStatus(p.toString());
I wonder is anybody could advice if/what I'm doing wronfg and how I should go about resolving this?
First, the real answer: step back for a moment. Don't start with JUnit and Mockito and your production code as input. Rather have a look into a tutorial (like here) that step-by-step explains all the relevant elements and how to "bring" them together.
In your case, the are various problems with your code:
Why are you using PowerMock? Try to go with "plain vanilla" Mockito. If your production code is so that it requires PowerMock, rather consider to rework your production instead of turning to PowerMock.
You seem to really not know where/how to apply mocking. In other words: you only mock the elements that you need to control when running your code under test. And you only use mocking, if you can't control them otherwise. Meaning: you almost never mock a list - you simply create a "normal" list to then add the things that this list should contain.
Creating a mock allows to invoke methods on that mock object. But by default, any method that returns something will return null (or maybe an empty collection, or 0 for primitive return types, see here for details). Thus you rather need a statement such as when(mockedCache.getStatus("some string")).thenReturn(someResult).

Writing unit tests with mocking of static methods in java

I'm new to writing tests in general. The class that I need to test has one method and it needs to be tested:
public String run(final Map<String, Dataset> datasets)
throws ApiException {
final String sourcePath = ElementsUtil.getElementFromDatasets(inputElementNames.get(0), datasets).getValue();
final String destinationPath = ElementsUtil.getElementFromDatasets(inputElementNames.get(1), datasets).getValue();
final File source = new File(sourcePath);
final File destination = new File(destinationPath);
if (source.exists()) {
if (source.isDirectory()) {
final IOFileFilter filter = new WildcardFileFilter(pattern);
final Iterator<File> it = FileUtils.iterateFiles(source, filter, null);
while (it.hasNext()) {
final File file = it.next();
moveFileToDirectory(file, destination);
}
} else {
moveFileToDirectory(source, destination);
}
} else {
LOGGER.error("Source file/folder at path {} doesn't exist.", sourcePath);
}
return "0";
}
At first, with my limited knowledge of writing unit tests, my unit test looked like this:
#Test(description = "Test in case the source is a file.")
public void moveFileTest1() {
// setup
final String fileName = UUID.randomUUID().toString() + ".txt";
final String folderName = UUID.randomUUID().toString();
final Element source = new Element("source", "./" + fileName);
final Element destination = new Element("destination", "./" + folderName);
...
final Path sourcePath = Paths.get(source.getValue());
final Path destinationPath = Paths.get(destination.getValue());
final Path fileDestination = Paths.get(destination.getValue() + "/" + fileName);
try {
Files.createFile(sourcePath);
Files.createDirectory(destinationPath);
// exercise
moveFile.run("", datasets, null);
// verify
Assert.assertEquals(Files.exists(fileDestination), true);
Assert.assertEquals(Files.exists(sourcePath), false);
} catch (ApiException | IOException e) {
LOGGER.error("Exception : ", e);
} finally {
// teardown
try {
Files.deleteIfExists(sourcePath);
} catch (final IOException e) {
LOGGER.error("Exception in teardown: ", e);
}
try {
Files.deleteIfExists(fileDestination);
} catch (IOException e) {
LOGGER.error("Exception in teardown: ", e);
}
try {
Files.deleteIfExists(destinationPath);
} catch (IOException e) {
LOGGER.error("Exception in teardown: ", e);
}
}
}
After reading some articles about unit testing I found out that my test isn't exactly testing a unit since my method depends on different util methods. Also I found out about mocking objects in tests and how everything should be mocked. My question is: Should I use mocking in each of these util methods/new object calls etc. or is there a different approach? How would you test this piece of code?
What you are doing is called integration testing. Integration testing is testing an object/method without mocking nothing, on real data or data that you produce during the test itself. What integration testing does is to test both your unit, the units that your unit uses and the flow of the use. If you would like to test only your unit you should mock all other units that your unit uses and create flows of those units doing their job successfully or not. Meaning you should replicate a test in which a used unit throws an exception/returns a value that you don't expect as a good value(only if it really can do it) and return a value that you do expect and now how to work with.
Usually when writing tests you do both kinds of testing, unit and integration tests
There is such good test principle as "Don't Mock What You Don't Own". What doesn't it mean? It means your shouldn't mock/stub any interface which you cannot control, even if this interface has been written by your company, but not by your team.
But what about writing unit tests, not integration tests, where you may want to mock all classes except a class which is being tested? Indeed, this is a really tricky question. And an answer more about system design than about testing. You may read about how the issue could be resolved here and here.
What does it means for your?
As your mentioned ElementsUtil has been written by you, so this class exactly should be mocked. How? It depends is it the legacy code or new code which you're writing now. If you have legacy code - then you need PowerMock, otherwise you may change design and use an instance of ElementsUtil.
For example, divide the ElementsUtil class into three classes: Elements- interface, ElementsImpl- implementation, ElementsUtil - class with static access to keep compatibility. The ElementsUtil may have method
public static Elements getInstance()
And the method could be used by class which contains run method in constructor. But you may provide either constructor with parameter or setter. By the way, as I remember Mockito can inject mocks into private fields. After this refactoring you don't need PowerMock and can use only Mockito.
Now about FileUtils. The class doesn't belong to you, so if follow good practice, then this class should be mock. But FileUtils works with file and it will already be integration tests. So answer - the FileUtils should be wrapped by a new your class.

Categories