How to mock a method in PowerMock? - junit

I'm using PowerMock 1.4.12 and JUnit 4.8.1. I'm having a problem getting a method to return the data I want it to. I have
#Before
public void setUp() throws Exception
{
...
userService = createMock(UserService.class);
loginController.setUserService(userService);
…
}
#Test
public final void testAuthenticateForLoggedInAdmin()
{
authorities.add(adminAuthority);
final User user = new User();
user.setUserName("userName");
user.setPassword("password");
user.setFirstName("firstName");
user.setMiddleName("middleName");
user.setLastName("lastName");
user.setUrl("localhost");
user.setId("id1");
final TestsubcoAuthenticationUser principal = new TestsubcoAuthenticationUser(user.getUserName(),
user.getPassword(),
true,
true,
true,
true,
authorities,
user.getFirstName(),
user.getLastName(),
user.getMiddleName(),
user.getUrl(),
user.getId(),
null,
null,
null);
authentication = new UsernamePasswordAuthenticationToken(principal, new Object(), authorities);
securityContext.setAuthentication(authentication);
mockStatic(SecurityContextHolder.class);
expect(SecurityContextHolder.getContext()).andReturn(securityContext);
expect(userService.findByUsernameAndUrl(user.getUserName(), user.getUrl())).andReturn(user);
...
String result = loginController.authenticate();
but when my method in question gets called from within the controller,
final User user = userService.findByUsernameAndUrl(sbUser.getUsername(), sbUser.getUrl());
the return value is null instead of the object I specified. Any theories behind why this is or suggestions for troubleshooting further? I have verified through debugging that the String parameter values passed in the controller are the same as what I specify in the "expect" method.

It looks like you did not call replay on your mocks.
replay

but when my method in question gets called from within the controller,
I am not sure what your mean, method in question gets called from within the controller.
When you call the method ,your userService object should be the same object which you mocked. also check the argument value if same.
I you dont find this usefull, share more detail in what your trying to unit test here.

Related

How to make a valid test using the mockito library?

The context
I have a simple method that I'm testing using the mockito library.
The problem
I have a error:
"[MockitoHint] ReceiveServiceTest.testGetFileDto (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at .ReceiveServiceTest.testGetFileDto(ReceiveServiceTest.java:46)
[MockitoHint] ...args ok? -> at ReceiveService.getFileDto(ReceiveService.java:28)
I dont understand way.
The code
#RunWith(MockitoJUnitRunner.class)
public class ReceiveServiceTest {
private List<File> filePaths = new ArrayList<>();
#InjectMocks
private ReceiveService receiveService;
#Mock
private FindFiles findfiles;
#Mock
private ReadByte readByte;
#Before
public void before() {
filePaths.add(new File("d://folder//test1_message_received"));
filePaths.add(new File("d://folder//test2_message_received"));
filePaths.add(new File("d://folder//test3_message_received"));
}
#Test
public void testGetFileDto() throws IOException {
// Given
byte[] resultByteArr = new byte[1028];
when(findfiles.getPathFiles()).thenReturn(filePaths);
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
List<MessageDTO> result = receiveService.getFileDto();
//some assert
}
method
#Autowired
private FindFiles findFiles;
#Autowired
private ReadByte readByte;
public List<MessageDTO> getFileDto() throws IOException {
List<MessageDTO> fileDtos = new ArrayList<>();
for (File file : findFiles.getPathFiles()) {
fileDtos.add(new MessageDTO(Base64.getEncoder().encode(readByte.readByteArrFromFile(new File(file.getPath()))),
file.getName(), "zip", null));
}
return fileDtos;
}
I think mocks are not being initialized. Please initialize the mocks in the #Before method.
#Before
public void init() {
initMocks(this);
}
This should solve the problem I guess.
Here is solution for my problem. I added foreach loop. Now the mock works, but byte [] is different than what it should return.
// Given
byte[] mockByteArr = new byte [2048];
when(findfiles.getPathFiles()).thenReturn(filePaths);
for (File filePath : filePaths) {
when(readByte.readByteArrFromFile(new File(filePath.getPath()))).thenReturn(mockByteArr);
}
//When
List<MessageDTO> result = receiveService.getFileDto();
//Then
assertEquals(3, result.size());
assertEquals(mockByteArr, result.get(1).getContent());
Your problem is, that you create a new object in the following line:
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
Mockito needs to know which real object is passed to the method so that it can return the appropriate thenReturn-value. So if you pass the actual reference into it, your code will work, but also only if you specify all the values which are listed. Otherwise you may get a NullPointerException.
By the way, calling new File(file.getPath()) seems redundant to me. You can just use file instead.
So with the following your code might work better:
when(readByte.readByteArrFromFile(filePaths.get(0)).thenReturn(resultByteArray);
but then you need to specify it for all entries.
Alternatively, use a Matcher instead:
when(readByte.readByteArrFromFile(ArgumentMatchers.any(File.class))).thenReturn(resultByteArr);
or specify the actual argument matching you require as matchers can be very powerful in that regard.
Previously the answer contained the following, which is still true, but not as concise as the answer above:
It's been a long time since I last used mocks (and I am even proud of it ;-)).
The message already states that one should consult the javadoc and there I found the following:
Those are hints - they not necessarily indicate real problems 100% of the time.
Nonetheless, I believe the problem is with the following statement:
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
I think you need to specify a return for every entry in the filePaths or make the call more generic using Matchers.any() (or any other appropriate Matcher).

Assertion error expectation failure EasyMock

I have the next JUnit test, and it works fine, but finally in the verify it throws expectation failure. I think it is because the mocked PsPort is different of the PsPort that I send to the DataReader.
Is there any other way to test it?
#Test
public void testguardarMensaje() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, InstantiationException{
String datoTest = "1=123456";
Constructor<PsPort> constructor = PsPort.class.getDeclaredConstructor(new Class[] {String.class});
constructor.setAccessible(true);
PsPort port = constructor.newInstance("middleware.conf");
PsPort drMock;
int hash1 = datoTest.hashCode();
String hashString1 = String.valueOf(hash1);
String combinedIdDataHashString1 = datoTest +"="+ hashString1;
drMock = strictMock(PsPort.class);
byte[] datoByte = port.encriptarDesencriptarMensaje(combinedIdDataHashString1.getBytes(), Cipher.ENCRYPT_MODE);
drMock.guardarDato(datoByte);
replayAll();
int hash = datoTest.hashCode();
String hashString = String.valueOf(hash);
String combinedIdDataHashString = datoTest +"="+ hashString;
byte[] datoByte2 = port.encriptarDesencriptarMensaje(combinedIdDataHashString.getBytes(), Cipher.ENCRYPT_MODE);
DatagramPacket paquete = new DatagramPacket(datoByte2,datoByte2.length);
paquete.getData();
DataReader dr = new DataReader(port, null, 100, "=", "C:/Users/Asier/Desktop/logs/");
dr.guardarMensaje(paquete, port);
verifyAll();
}
It is really confusing that you have two port objects. What is the sense of creating a mocked drPort; when you are then giving a "real" port object to your class under test?
You see: you either create a mock and pass that down to your code under test (and then you have to setup the mock for the expected behavior; which you can afterwards verify); or you only provide "real" objects to your code under test, but then you would normally do some kind of asserts on the results of calls to "code under test".
So, in that sense, it doesn't really matter that there is at least one problem in your code:
drMock.guardarDato(datoByte);
replayAll();
There should be a call to EasyMock.expectLastCall() after the method invocation on drMock; but as said: as the mocked object isn't really used, that doesn't matter, on the one hand. Because, if you added that statement, your test would always fail; since your un-used mock would never see the calls that you specified it to see.
In order to give you some guidance; this is how you do such kind of testing in general:
SomeClassYouNeed mockedThingy = createStrict/Nice(SomeClassYouNeed.class);
expect(mockedThingy.foo()).andReturn("whatever");
mockedThingy.bar();
expectLastCall();
replay (mockedThingy);
ClassUnderTest underTest = new ClassUnderTest(mockedThingy);
underTest.doSomething();
verify(mockedThingy)
Meaning: any "object" that
a) your "class under test" needs to do its work
b) you want/have to "control" in a certain way
needs to be mocked; including a "specification" of all expected method calls.
Then you provide the mocked things to your code under test; execute the method you want to test ... to finally verify that the mock saw the behavior that you specified for it.

Unable to mock URL class using PowerMockito/Mockito

I am trying to use PowerMockito to mock the creation of the java.net.URL class in my code that I'm testing. Basically, I want to prevent the real HTTP request from occurring and instead 1) check the data when the request is made and 2) supply my own test data back on a mocked response. This is what I'm trying:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ URL.class, MockedHttpConnection.class })
public class Test {
URL mockedURL = PowerMockito.mock(URL.class);
MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class);
...
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL);
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
...
}
The code that I want to test looks like this:
URL wlInvokeUrl = new URL(wlInvokeUrlString);
connection = (HttpURLConnection) wlInvokeUrl.openConnection();
Earlier in my test scenario I mock the wlInvokeUrlString to match "MyURLString". I've also tried using various other forms of the whenNew line, trying to inject the mock. No matter what I try, it never intercepts the constructor. All I want to do is "catch" the call to openConnection() and have it return my mocked HTTP connection instead of the real one.
I have mocked other classes ahead of this one in the same script and these are working as expected. Either I need a second pair of eyes (probably true) or there is something unique about the URL class. I did notice that if I use "whenNew(URL.class).withAnyArguments()" and change the "thenReturn" to "thenAnswer" I could get it to trigger. Only problem is I never get the URL call for my code. What I see is an invocation of the 3-argument constructor for URL.class with all nulls for the parameters. Could it be this class is from the Java runtime and is bootstrapped by the test runner? Any help is much appreciated.
It's a common mistake when use PowerMockito.whenNew.
Note that you must prepare the class creating the new instance of MyClass for test, not the MyClass itself. E.g. if the class doing new MyClass() is called X then you'd have to do #PrepareForTest(X.class) in order for whenNew to work
From Powermock wiki
So, you need a bit change your test and add to #PrepareForTesta class which create a new instance of URLlike:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ URL.class, MockedHttpConnection.class , ConnectionUser.class})
public class URLTest {
public class URLTest {
private ConnectionUser connectionUser;
#Before
public void setUp() throws Exception {
connectionUser = new ConnectionUser();
}
#Test
public void testName() throws Exception {
URL mockedURL = PowerMockito.mock(URL.class);
MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class);
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL);
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
connectionUser.open();
assertEquals(mockedConnection, connectionUser.getConnection());
}
}
where:
public class ConnectionUser {
private String wlInvokeUrlString = "MyURLString";
private HttpURLConnection connection;
public void open() throws IOException {
URL wlInvokeUrl = new URL(wlInvokeUrlString);
connection = (HttpURLConnection) wlInvokeUrl.openConnection();
}
public HttpURLConnection getConnection() {
return connection;
}
}
I'm not sure the difference between .withParameterTypes(x) and .withArguments(x) but I believe you need to set it up as follows for your code to work. Give it a try and let me know if this doesn't help.
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
PowerMockito.whenNew(URL.class).withArguments(Mockito.anyString()).thenReturn(mockedURL);
The problem is that the arguments of the real call are not matching with the expected in your mock.
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL) will return mockedURL only the call is new URL("MyURLString").
If you change it to:
PowerMockito.whenNew( URL.class ).withParameterTypes( String.class )
.withArguments( org.mockito.Matchers.any( String.class ) ).thenReturn( mockedURL );
It will catch any string passed to the constructor URL(String) (even null) and return your mock.
When you tried
"whenNew(URL.class).withAnyArguments()" and change the "thenReturn" to
"thenAnswer" I could get it to trigger. Only problem is I never get
the URL call for my code. What I see is an invocation of the
3-argument constructor for URL.class with all nulls for the
parameters.
PowerMock will try to mock all constructors (org.powermock.api.mockito.internal.expectation.DelegatingToConstructorsOngoingStubbing.InvokeStubMethod at line 122) then it calls the first one (with 3 arguments) and mock its answer. But the subsequent calls will return the already mocked one because you told it to mock for any arguments. That's why you see just one call with null, null, null in your Answer.

Mocked ExecutorService always returns the same mocked Future

I implemented a mock ExecutorService to return the result instantly without the need to create threads:
public static ExecutorService createMock() throws Exception {
ExecutorService executorServiceMock = EasyMock.createMock(ExecutorService.class);
Future future = EasyMock.createMock(Future.class);
Capture<Callable<?>> callableCapture = new Capture<>();
EasyMock.expect(executorServiceMock.submit(EasyMock.<Callable<?>>capture(callableCapture))).andReturn(future).anyTimes();
EasyMock.expect(future.get()).andAnswer(() -> callableCapture.getValue().call()).anyTimes();
executorServiceMock.shutdown();
EasyMock.expectLastCall().anyTimes();
EasyMock.replay(future, executorServiceMock);
return executorServiceMock;
}
The problem is that it always returns the same [mocked] Future object. I need to return the new instance of Future mock based on the callable object passed to executorServiceMock.submit()
I tried to use PowerMock.expectNew(Future.class) but it complained "No constructor found in class 'java.util.concurrent.Future' with parameter types: [ ]"
First of all "Don't mock type you don't own!". By link you may find several reasons why you shouldn't do it.
But, if you really want to do it, then replace return mock via answer witch will create a new nock each time:
EasyMock.expect(executorServiceMock.submit(EasyMock.<Callable<?>>capture(callableCapture))).andAnswer(() -> {
Future future = EasyMock.createMock(Future.class);
EasyMock.expect(future.get()).andAnswer(() -> callableCapture.getValue().call()).anyTimes();
EasyMock.replay(future);
return future;
}).anyTimes();
By the way, you cannot expected PowerMock.expectNew(Future.class) because Futureis interface and cannot be created.
The capture will always return the last captured object. In your case, you seem to want to synchronize the submission and the get.
So, I think you should do the following:
ExecutorService executorServiceMock = createMock(ExecutorService.class);
expect(executorServiceMock.submit(EasyMock.<Callable<?>>anyObject()))
.andAnswer(() -> {
Future future = createMock(Future.class);
Object value = ((Callable<?>) getCurrentArguments()[0]).call();
expect(future.get()).andReturn(value);
replay(future);
return future;
})
.anyTimes();
executorServiceMock.shutdown();
expectLastCall().anyTimes();
replay(executorServiceMock);
return executorServiceMock;
Below code fixed my issue.
#Mock
private ThreadPoolExecutor executorPool;
Future<String> result = CompletableFuture.completedFuture("S3PATH");
when(executorPool.submit(Mockito.<Callable<String>> anyObject())).thenReturn(result);

Can a second parameter be passed to Controller constructors?

Castle Windsor passes the registered concrete type to Controller's constructors. A typical implementation (no pun intended) is:
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository");
}
_deptsRepository = deptsRepository;
}
I need to pass the ctor a second parameter, if possible, so that I can pass that val on to the Repository constructor (I know: tramp data alert, but I don't know if there's a straightforward way around it:
public DepartmentsController(IDepartmentRepository deptsRepository, int DBInstance)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository");
}
_deptsRepository = deptsRepository(DBInstance);
}
REPOSITORY
public DepartmentRepository(int dbInst)
{
string connStr = string.Format("Phoo{0}Bar", dbInst);
using (var conn = new OleDbConnection(connStr))
{
using (var cmd = conn.CreateCommand())
{
. . .
Is it possible to tweak what Castle Windsor sends to the Controller constructor this way? If so, how?
AND/BUT: For this to be of any value (to me, anyway), I need to be able to get the int val (that will be passed to the Controller) from the URL the client sends. IOW, if the client asks the server for data via:
http://locohost:4242/Platypus/GetAll/1
I need to pass a "1" as the second argument to PlatypusController.
If the user asks the server for data via:
http://locohost:4242/Platypus/GetAll/42
I need to pass a "42" as the second argument to PlatypusController.
etc.
This is what I did to solve my Controller/Repository data context Dilemma:
0) Added a database context argument to the Controller's routing attribute. IOW, this:
[Route("api/HHSUsers/GetAll")]
...got changed to this:
[Route("api/HHSUsers/GetAll/{dbContext=03}")]
1) Passed that database context arg to the Repository. To wit, this:
return _hhsusersrepository.GetAll();
...got changed to this:
return _hhsusersrepository.GetAll(dbContext);
...so that the Controller method is now:
[Route("api/HHSUsers/GetAll/{dbContext=03}")]
public IEnumerable<HHSUsers> GetAllHHSUsersRecords(int dbContext)
{
return _hhsusersrepository.GetAll(dbContext);
}
2) Changed the corresponding method in the Repository interface from:
IEnumerable<HHSUsers> GetAll();
...to this:
IEnumerable<HHSUsers> GetAll(string dbContext);
3) Changed the Repository method from this:
public HHSUsersRepository()
{
// All the data is loaded here in the ctor
}
public IEnumerable<HHSUsers> GetAll()
{
return hhsusers;
}
....to this:
public IEnumerable<HHSUsers> GetAll(string dbContext)
{
LoadHHSUsers(dbContext);
return hhsusers;
}
private void LoadHHSUsers(int dbContext)
{
string connStr = string.Format("Foo{0}Bar", dbContext);
// The same as previously from this point on, except that this:
// using (var conn = new OleDbConnection(#"Foo Bar Phoo Bar etc"...
// becomes:
// using (var conn = new OleDbConnection(connStr))
4) Tack the dbcontext val to the end of the URL when calling the method, so that it is this:
http://localhost:28642/api/HHSUsers/GetAll/42
...instead of this:
http://localhost:28642/api/HHSUsers/GetAll
If the data context to use is "03" I can omit the dbcontext arg from the URL, as 03 is the default value I set when I appended "=03" to the Controller's "dbContext" routing attribute arg.
I know some fancy-pants propeller-heads will find fault with this for some reason (for one reason because of the tramp data going here and there and everywhere like a hobo on steroids), but my response is the same as that of an athlete who is getting trash-talked by an opposing player and yet whose team is winning: just point at the scoreboard. IOW, this works for me, so that's pretty much all I care about. Style points are for runway models and, again, fancy-pants propeller-heads (AKA Star-Bellied Sneeches (as opposed to us plain
cats with the unstarred bellies)); see "The perfect is the enemy of the good."
This simple way has that self-same benefit -- of being (relatively) simple to grok and, thus, modify/refactor as necessary. Inelegant? Sure, but so was Joe Kapp.