I want to cover a logic, that creates files with unit tests. Is it possible to mock File class and to avoid actual file creation?
Mock the constructor, like in this example code. Don't forget to put the class that will invoke the "new File(...)" in the #PrepareForTest
package hello.easymock.constructor;
import java.io.File;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest({File.class})
public class ConstructorExampleTest {
#Test
public void testMockFile() throws Exception {
// first, create a mock for File
final File fileMock = EasyMock.createMock(File.class);
EasyMock.expect(fileMock.getAbsolutePath()).andReturn("/my/fake/file/path");
EasyMock.replay(fileMock);
// then return the mocked object if the constructor is invoked
Class<?>[] parameterTypes = new Class[] { String.class };
PowerMock.expectNew(File.class, parameterTypes , EasyMock.isA(String.class)).andReturn(fileMock);
PowerMock.replay(File.class);
// try constructing a real File and check if the mock kicked in
final String mockedFilePath = new File("/real/path/for/file").getAbsolutePath();
Assert.assertEquals("/my/fake/file/path", mockedFilePath);
}
}
try PowerMockito
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
#PrepareForTest(YourUtilityClassWhereFileIsCreated.class)
public class TestClass {
#Test
public void myTestMethod() {
File myFile = PowerMockito.mock(File.class);
PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(myFile);
Mockito.when(myFile.createNewFile()).thenReturn(true);
}
}
I'm not sure if it's possible, but I have had such a requirement, and I solved it by creating a FileService interface. So instead of creating/accessing files directly, you add an abstraction. Then you can easily mock this interface in your tests.
For instance:
public interface FileService {
InputStream openFile(String path);
OutputStream createFile(String path);
}
Then in your class using this:
public class MyClass {
private FileService fileService;
public MyClass(FileService fileService) {
this.fileService = fileService;
}
public void doSomething() {
// Instead of creating file, use file service
OutputStream out = fileService.createFile(myFileName);
}
}
And in your test
#Test
public void testOperationThatCreatesFile() {
MyClass myClass = new MyClass(mockFileService);
// Do your tests
}
This way, you can even mock it without any mock libraries.
Related
I have a ServiceWebClientInterface.java like this
import reactor.core.publisher.Mono;
public interface ServiceWebClientInterface {
Mono<String> apiCall();
}
MyClass.java
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
public class MyClass extends AbstractGatewayFilterFactory<MyClass.Config> {
private final ServiceWebClientInterface serviceWebClientInterface;
MyClass(final ServiceWebClientInterface serviceWebClientInterface) {
this.serviceWebClientInterface = serviceWebClientInterface;
}
#Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
return serviceWebClientInterface.apiCall().flatMap(response -> {
if (!"Valid".equals(response)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
});
};
}
public static class Config {
// Put the configuration properties
}
}
I'm trying to unit test myMethod using StepVerifier, but I am not able to execute statements inside the inner lambda function of myMethod.
MyClassTest.java
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
class MyClassTest {
#Mock
ServiceWebClientInterface mockServiceWebClientInterface;
#Mock
private ServerWebExchange mockServerWebExchange;
#Mock
private GatewayFilterChain mockGatewayFilterChain;
#Mock
private ServerHttpResponse mockServerHttpResponse;
#BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
void test_apply_forValid() {
when(mockServiceWebClientInterface.apiCall()).thenReturn(Mono.just("Valid"));
MyClass.Config config = new MyClass.Config();
MyClass myClass = new MyClass(mockServiceWebClientInterface);
GatewayFilter gatewayFilter = myClass.apply(config);
Mono<Void> response = gatewayFilter.filter(mockServerWebExchange, mockGatewayFilterChain);
StepVerifier.create(response).expectComplete();
verify(mockServiceWebClientInterface).apiCall();
verify(mockGatewayFilterChain).filter(mockServerWebExchange);
}
#Test
void test_apply_forInValid() {
when(mockServiceWebClientInterface.apiCall()).thenReturn(Mono.just("InValid"));
when(mockServerWebExchange.getResponse()).thenReturn(mockServerHttpResponse);
MyClass.Config config = new MyClass.Config();
MyClass myClass = new MyClass(mockServiceWebClientInterface);
GatewayFilter gatewayFilter = myClass.apply(config);
Mono<Void> response = gatewayFilter.filter(mockServerWebExchange, mockGatewayFilterChain);
StepVerifier.create(response).expectComplete();
verify(mockServiceWebClientInterface).apiCall();
verify(mockServerHttpResponse).setStatusCode(eq(HttpStatus.FORBIDDEN));
verify(mockServerHttpResponse).setComplete();
verify(mockGatewayFilterChain, never()).filter(mockServerWebExchange);
}
}
Please find the complete code above, When I run the tests I observe that the inner lambda function does not get invoked using the step verifier.
I guess you want to test the class that implements MyLambda interface.
For sure you inject there serviceWebClientInterface as mentioned on code snippet.
To unit test that class, you should mock the serviceWebClientInterface.apiCall() and verify if it was called. As an addition to your actual code snippet.
You can use Mockito library for that purpose.
create a mock:
given(serviceWebClientInterface).willReturn(Mono.just("some text"));
then verify if it is called:
verify(serviceWebClientInterface).apiCall()
I was able to fix this issue by using
StepVerifier.create(response).verifyComplete();
and mocking chain.filter(exchange);
I want to test the following example code:
public class Example {
...
public void doStuff() {
...
Lift lift = new Lift();
lift.call(5);
...
}
...
}
How can I 'intercept' lift.call(5)?
Generally I would use when(lift.call(anyInt()).thenReturn(...), but I have no reference to the Lift object.
You can't do it with mockito alone. The cleanest solution is to refactor your code so you can have access to it. However if that's not an option then "power mockito" is what you want. Grab "powermock-api-mockito"+"powermock-module-junit4" and then something like this will do the trick:
import static org.mockito.Mockito.verify;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(Example.class)
public class ExampleTest {
private Example testSubject;
#Mock
private Lift lift;
#Test
public void testDoStuff() throws Exception {
testSubject.doStuff();
verify(lift).call(5);
}
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.whenNew(Lift.class).withNoArguments().thenReturn(lift);
testSubject = new Example();
}
}
Can you modify the Example class? If yes, the simplest way would be to extract the Lift dependency and provide it via constructor. Like this:
public class Example {
private final Lift lift;
public Example(Lift lift) {
this.lift = lift;
}
public void doStuff() {
this.lift.call(5);
}
}
Then you can stub lift as you want since now you have access to the instance.
I cannot mock a method defined as default in an interface. Can anyone help me here?
The interface has default method providing a logger.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public interface Loggable {
default Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
}
It is used this way:
public class AppShowOff implements Loggable{
public void doMagic() {
logger().debug("It works");
System.out.println("Works");
}
}
now I would like to write a test proving that debug method has been called.
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
public class AppShowOffTest {
#Test
public void doMagic() {
Logger loggerMock = mock(Logger.class);
Loggable loggableMock = mock(Loggable.class); // <- not needed, but I also tried this way
// mocks done
AppShowOff app = new AppShowOff();
AppShowOff appSpy = Mockito.spy(new AppShowOff());
when(loggableMock.logger()).thenReturn(loggerMock);
when(appSpy.logger()).thenReturn(loggerMock);
app.doMagic();
verify(loggerMock, times(1)).debug(any());
}
}
as you can see I have tried to mock the default method in two ways:
when(loggableMock.logger()).thenReturn(loggerMock);
when(appSpy.logger()).thenReturn(loggerMock);
but it does not work. The result is:
Wanted but not invoked: logger.debug();
-> at so.AppShowOffTest.doMagic(AppShowOffTest.java:29) Actually, there were zero interactions with this mock.
Here:
AppShowOff app = new AppShowOff();
AppShowOff appSpy = Mockito.spy(new AppShowOff());
That first app ... is never used, besides to call your method under test doMagic().
The simple answer: drop app completely, and invoke appSpy.doMagic().
I got the problem that after mocking the java.io.File(File parent, String childName) constructor with PowerMock still the original constructor is invoked.
Here is the coding:
Class under test:
import java.io.File;
public class ConstructChildFile {
public File newChildFile(File parent, String childName) {
return new File(parent, childName);
}
}
Test case:
import java.io.File;
import org.easymock.EasyMock;
import org.junit.Test;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
#PrepareForTest({File.class, ConstructChildFile.class})
public class TestConstructorMocking {
#Test
public void test() throws Exception {
File mockedFolder = EasyMock.createMock(File.class);
File mockedChild = EasyMock.createMock(File.class);
PowerMock.expectNew(File.class, mockedFolder, "test").andReturn(mockedChild).anyTimes();
EasyMock.replay(mockedFolder, mockedChild);
PowerMock.replay(File.class, ConstructChildFile.class);
System.out.println(new ConstructChildFile().newChildFile(mockedFolder, "test"));
}
}
So when ConstructChildFile.newChildFile(...) is invoked I expect to get the mockedChild instance since I mocked the constructor a few lines above. Still this does not happen - the actual constructor is called.
The test fails with:
java.lang.NullPointerException
at java.io.File.<init>(File.java:363)
at test.ConstructChildFile.newChildFile(ConstructChildFile.java:7)
at test.TestConstructorMocking.test(TestConstructorMocking.java:24)
...
Any ideas?
I have a DummyResource class and a DummyTarget file, and a test class TestDummyResource as below, but the mocked object DummyResource dr = mock(DummyResource.class) only works when I call the constructor inside a normal class, when it's called in an anonymous class, it's calling the actual constructor instead of using the mocked object.
Versions:
powermock 1.4.12 mockito 1.9.0 junit 4.8.2
DummyTarget.java:
import java.io.IOException;
import java.io.OutputStream;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
public class DummyTarget {
public StreamingOutput testMocking() {
return new StreamingOutput() {
#Override
public void write(OutputStream arg0) throws IOException, WebApplicationException {
new DummyResource();
}
};
}
}
DummyResource.java:
package com.smin.dummy;
public class DummyResource {
public DummyResource() {
System.out.println("mock failure");
}
}
TestDummyResource.java:
package com.smin.dummy;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
import org.junit.Before;
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;
#RunWith(PowerMockRunner.class)
#PrepareForTest({DummyResource.class,DummyTarget.class})
public class TestDummyResource {
#Before
public void setUp() throws Exception {
DummyResource dr = mock(DummyResource.class);
PowerMockito.whenNew(DummyResource.class).withNoArguments().thenReturn(dr);
}
#Test
public void testMocked() throws WebApplicationException, IOException {
new DummyResource(); // it uses the mocked object dr here,
//doesn't print "mock failure"
StreamingOutput sop = new DummyTarget().testMocking();
sop.write(null); // it calls DummyResource's constructor,
// prints ""mock failure"" here
}
}
You need to have prepared the class calling the constructor, not the class on which the constructor is called, the following should fix you up:
#PrepareForTest(DummyTarget.class)
For more information check this page.
It looks like an anonymous class may inherit the package of the class that defines it. Can you try the wildcard form of PrepareForTest?:
#PrepareForTest("com.smin.dummy.*")
If that doesn't work, you could try the shotgun PrepareEverythingForTest Annotation.
Actually, you have to prepare for test the class that makes the constructor call, not the class on which the constructor was called. See https://github.com/jayway/powermock/wiki/MockConstructor.
In your case, you should use #PrepareForTest(DummyTarget.class)
I had the same problem, and resolved it with using whenNew with fully qualified name. The fully qualified name of an inner anonymous class in your case is:
DummyTarget.class + "$1"
so you should create a mock of that class:
DummyResource dr = mock(Class.forName(DummyTarget.class + "$1"));
and it will work for you.
Also, don't forget to prepare the DummyTarget class:
#PrepareForTest(DummyTarget.class)
Hope it helped =]