I want use a Powermock to test a private method (one) but this private method relies on another private method (two).
So I have to mock the other private method. But while I am debugging it, it turns out that the private method one is not calling the mocked private method (two) and if I run instead of debugging it throws out exception:
1matchers expected, 2 recorded.
private int getCurrentLocaleID(WebIServerSession currentSession, String preferenceName) {
String prefLocaleID = getUserPreference(currentSession, preferenceName);
int lcid;
if (HTTPHelper.isDefaultLocale(prefLocaleID)) {
prefLocaleID = _appContext.getBrowserHeaderLocaleId();
}
try {
lcid = Integer.parseInt(prefLocaleID);
} catch (NumberFormatException nfe) {
lcid = DEFAULT_LCID; // default behavior from old session manager
}
return lcid;
}
#Test
public void getCurrentLocaleID() throws Exception {
PowerMockito.mockStatic(HTTPHelper.class);
WebAppSessionManagerImpl webAppSessionMangerImpl2 = PowerMockito.spy(new WebAppSessionManagerImpl(appConext));
given(HTTPHelper.isDefaultLocale("1")).willReturn(true);
given(HTTPHelper.isDefaultLocale("0")).willReturn(false);
given(appConext.getBrowserHeaderLocaleId()).willReturn("1");
PowerMockito.doReturn("1").when(webAppSessionMangerImpl2, "getUserPreference", anyObject(), anyString());
int result = Whitebox.invokeMethod(webAppSessionMangerImpl2, "getCurrentLocaleID", webIserverSession, "test");
assertEquals(result, 1);
}
Dont test private methods. If you have to, that means that your class is doing too much than it supposed to and it does not comply with the Single Responsibility Principle.
This is a chance for some refactoring and isolation of logic in specialized class like something follwing:
public class SpecializedClass{
private Context context;
public SpecializedClass(Context context){
this.context = context;
}
public int getCurrentLocaleID(WebIServerSession currentSession, String preferenceName) {
String prefLocaleID = getUserPreference(currentSession, preferenceName);
int lcid;
if (HTTPHelper.isDefaultLocale(prefLocaleID)) {
prefLocaleID = _appContext.getBrowserHeaderLocaleId();
}
try {
lcid = Integer.parseInt(prefLocaleID);
} catch (NumberFormatException nfe) {
lcid = DEFAULT_LCID; // default behavior from old session manager
}
return lcid;
}
String getUserPreference(Session session, String preferenceName){..}
}
Now haiving the method public and the getUserPreference marked as package level, the test would look something like:
#Test
public void getCurrentLocaleID() throws Exception {
PowerMockito.mockStatic(HTTPHelper.class);
SpecializedClass specializedClassSpy = Mockito.spy(new SpecializedClass(appConext));
given(HTTPHelper.isDefaultLocale("1")).willReturn(true);
given(HTTPHelper.isDefaultLocale("0")).willReturn(false);
given(appConext.getBrowserHeaderLocaleId()).willReturn("1");
Mockito.doReturn("1").when(specializedClassSpy)
.getUserPreference(webIserverSession, "test");
int result = specializedClassSpy.getCurrentLocaleID(webIserverSession, "test");
assertEquals(result, 1);
}
Related
UPDATED QUESTION TO CLARIFY
I have a class with a void method. This method will call a service which will lock user after X failed attempts to unlock a door.
Here are my classes:
#Stateless(name = "DoorServiceEjb")
public class DoorServiceEjb {
private static final int EXPIRY_PERIOD = 1;
private static final int MAX_ATTEMPTS = 3;
private LoadingCache<String, Integer> attemptsCache;
public DoorServiceEjb() {
super();
attemptsCache = CacheBuilder
.newBuilder()
.expireAfterWrite(EXPIRY_PERIOD, TimeUnit.MINUTES)
.build(new CacheLoader<String, Integer>() {
#Override
public Integer load(String key) throws Exception {
return 0;
}
}
);
}
public boolean isLockedOut(String key) {
try {
boolean returnValue = attemptsCache.get(key) >= MAX_ATTEMPTS;
return returnValue;
}catch (ExecutionException e) {
return false;
}
}
}
public class Door {
#EJB(beanName = "DoorServiceEjb")
private DoorServiceEjb doorServiceEjb;
public void unlock(String user) {
if (doorServiceEjb.isLockedOut(user)) {
throw new RuntimeException("Locked");
}
// Do something else
}
}
I am expecting that unlock() method to throw RuntimeException if user attempts to call it with invalid username 3 or more times.
MY QUESTIONABLE SOLUTION
Here is my solution to test but I am not sure if this is correct. I was always under impression that asserts and verifications are called always against #InjectMock objects (called subject under test) whereas #Mock objects are just used to mock internal dependencies inside #InjectMock object (basically, they are used to help test setup and run).
My solution, on the contrary, does not use #InjectMock at all and does verify and assert against #Mock object.
And finally, it still fails because last assert fails.
#ExtendWith(MockitoExtension.class)
public class DoorTest {
#Mock
private Door door;
#Test
public void test_3FailedUnlockAttempts_ShouldLock() {
Mockito.doNothing().doNothing().doThrow(RuntimeException.class).when(door).unlock(anyString());
door.unlock(anyString());
door.unlock(anyString());
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
door.unlock(anyString()); // should throw RuntimeException
});
Mockito.verify(door, times(3)).unlock(anyString());
//this fails, the exception message is null rather than "Unlocked"
Assertions.assertTrue(exception.getMessage().contains("Locked"));
}
}
I have method that is called by another service and it just change one of the field for some rows in database. Method looks like this:
void errorOrders() {
List<Orders> orders = OrderDAO.getErrorOrders(); //select only fields with status 'error'
orders.forEach(order -> order.setStatus(OrderStatus.NEW);
//some logging etc.
}
Is there any way to unit test this method? Can I inject myself inside this method and check if orders status was changed?
Cheers!
I would recommend you refactor your class to make your code testable. Ideally you would inject the dependency that represents the OrderDAO:
class ErrorChecker {
private final OrderDAO orderDAO;
public ErrorChecker(OrderDAO orderDAO) {
this.orderDAO = orderDAO;
}
public void errorOrders() {
List<Orders> orders = orderDAO.getErrorOrders();
orders.forEach(order -> order.setStatus(OrderStatus.NEW);
}
}
Then your test code would look like:
#Test
void testErrorOrders() {
Order order1 = mock(Order.class);
Order order2 = mock(Order.class);
OrderDAO orderDAO = mock(OrderDAO.class);
when(orderDAO.getErrorOrders()).thenReturn(List.of(order1, order2));
ErrorChecker errorChecker = new ErrorChecker(orderDAO);
errorChecker.errorOrders();
verify(order1).setState(OrderStatus.NEW);
verify(order2).setState(OrderStatus.NEW);
}
There are ways to mock static methods but I would recommend refactoring to inject the dependencies as it has many other benefits beside testability.
If you need to leave the method as static then you can still mock it (in v3.4+ of Mockito):
#Test
void testErrorOrders() {
try (MockedStatic mocked = mockStatic(OrderDAO.class)) {
mocked.when(OrderDAO.getErrorOrders()).thenReturn(List.of(order1, order2));
ErrorChecker errorChecker = new ErrorChecker(orderDAO);
errorChecker.errorOrders();
mocked.verify(order1).setState(OrderStatus.NEW);
}
}
#ismail and #khelwood already provided good answers.
If you mock the Object, you can control/see what happens to it
If you change an Object, where you can access the state via public methods, use those
If you change an Object whose state you cannot access with normal code, use Java Reflections to look at member variables.
If you set up Objects, that pass their data to streams and other output, you can put some additional streams etc in between. Use inheritance and reflection if necessary
Simple example of using Reflection on a shielded class:
package stackoverflow.simplefieldaccess;
public class ShieldedClass {
private int mStatus;
public ShieldedClass() {
mStatus = 666;
}
public void setStatus(final int pStatus) {
mStatus = pStatus; // usually with ints be careful and do checks here, but for the sake of simplicity we leave that out
}
#Override public String toString() {
return getClass().getSimpleName() + "[status:" + mStatus + "]";
}
}
Code to access it via reflection in a few ways:
package stackoverflow.simplefieldaccess;
import java.lang.reflect.Field;
import jc.lib.lang.reflect.JcFieldAccess;
public class SimpleFieldAccess {
public static void main(final String[] args) throws NoSuchFieldException, SecurityException {
final ShieldedClass so = new ShieldedClass();
System.out.println("Object.status before change: " + so);
so.setStatus(667);
System.out.println("Object.status after change: " + so);
System.out.println();
System.out.println("Accessing Object.status via Reflection...");
final Class<? extends ShieldedClass> cls = so.getClass();
final Field fieldToChance = cls.getDeclaredField("mStatus");
{
System.out.println("\nBad read access");
try { // will result in java.lang.IllegalAccessException
System.out.println("\tReading Object.status fiels via Reflection: " + fieldToChance.getInt(so));
throw new IllegalStateException("UNEXOECTED ERROR!");
} catch (final java.lang.IllegalAccessException e) {
System.out.println("\tAs expected: IllegalAccessException");
}
}
{
System.out.println("\nBad write access");
try { // will result in java.lang.IllegalAccessException
fieldToChance.set(so, Integer.valueOf(1337));
System.out.println("\tObject.status after change: " + so);
} catch (final java.lang.IllegalAccessException e) {
System.out.println("\tAs expected: IllegalAccessException");
}
}
{
System.out.println("\nGood manual read and write access");
final boolean isFieldOriginallyAccessible = fieldToChance.isAccessible();
try { // will result in java.lang.IllegalAccessException
if (!isFieldOriginallyAccessible) fieldToChance.setAccessible(true);
System.out.println("\tReading Object.status field via Reflection: " + fieldToChance.getInt(so));
fieldToChance.set(so, Integer.valueOf(4321));
System.out.println("\tObject.status after change: " + so);
} catch (final java.lang.IllegalAccessException e) {
e.printStackTrace();
} finally {
if (!isFieldOriginallyAccessible) fieldToChance.setAccessible(false);
}
}
{
System.out.println("\nGood automated read and write access");
try (JcFieldAccess fa = new JcFieldAccess(fieldToChance)) { // will result in java.lang.IllegalAccessException
System.out.println("\tReading Object.status field via Reflection: " + fieldToChance.getInt(so));
fieldToChance.set(so, Integer.valueOf(123));
System.out.println("\tObject.status after change: " + so);
} catch (final java.lang.IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
For reflections, when I want to access fields, I use my homebrew class that makes it easier to get access to the field and afterwards restore it to normal (last example above uses this):
package jc.lib.lang.reflect;
import java.io.Closeable;
import java.lang.reflect.AccessibleObject;
public class JcFieldAccess implements Closeable {
private final AccessibleObject mField;
private final boolean mIsAccessible;
public JcFieldAccess(final AccessibleObject pField) {
mField = pField;
mIsAccessible = mField.isAccessible();
if (!mIsAccessible) mField.setAccessible(true);
}
#Override public void close() {
if (mIsAccessible) return;
if (mField != null) mField.setAccessible(false);
}
}
The trick with this util class is that when used in a try-resource block, its close() method will get called automatically, whether the block fails or not. It's the same as having the close() or in this case setAccessible(false) call in the finally block, with some extra checks.
Let the class be:
class HandleErrorOrders {
private OrderDAO orderDAO;
HandleErrorOrders(final OrderDAO orderDAO) {
this.orderDAO = orderDAO;
}
public void errorOrders() {
List<Orders> orders = OrderDAO.getErrorOrders(); //select only fields with status 'error'
orders.forEach(order -> order.setStatus(OrderStatus.NEW);
//some logging etc.
}
}
You need to use assert methods to check end state.
To test, write something like:
class HandleErrorOrdersTest {
#Mock
private OrderDAO orderDAO;
#InjectMocks
private HandleErrorOrders handleErrorOrders;
#Test
void testErrorOrders() {
Order order1 = mock(Order.class);
Order order2 = mock(Order.class);
when(orderDAO.getErrorOrders()).thenReturn(List.of(order1, order2));
ErrorChecker errorChecker = new ErrorChecker(orderDAO);
errorChecker.errorOrders();
//asset checks
Assert.assertEquals(OrderStatus.NEW, order1.getStatus());
Assert.assertEquals(OrderStatus.NEW, order2.getStatus());
//verification checks
Mockito.verify(orderDAO).getErrorOrders();
}
}
public class MyXML {
private MessageParser messageParser;
private String valueA;
private String valueB;
private String valueC;
public MyXML (MessageParser messageParser) {
this.messageParser=messageParser;
}
public void build() {
try {
setValueA();
setValueB();
setValueC();
} catch (Exception e) {
e.printStackTrace();
}
}
private void setValueA() {
valueA = messageParser.getArrtibuteUsingXPath("SomeXPath1...");
}
private void setValueB() {
valueB = messageParser.getArrtibuteUsingXPath("SomeXPath2...");
}
private void setValueC() {
valueC = messageParser.getArrtibuteUsingXPath("SomeXPath...");
}
public String getValueA() {
return valueA;
}
public String getValueB() {
return valueB;
}
public String getValueC() {
return valueC;
}
}
So I need to use Mockito to test the builder method. Im fairly new to Mockito could someone give me some example code as to how I might write a test for the builder method?
If you want to suggest any ways I might change the design of the class or make it easier to test let me know.
To test build() you can try :
#RunWith(MockitoJUnitRunner.class)
public class YourTest {
#Mock
private private MessageParser messageParserMock;
// this one you need to test
private MyXML myXML;
#Test
public void test() {
myXML = new MyXML(messageParserMock);
// I believe something like this should work
Mockito.doAnswer(/* check mockito Answer to figure out how */)
.when(messageParserMock).getArrtibuteUsingXPath(anyString());
// you should do this for all your 3 getArrtibuteUsingXPath because setValueA(), setValueB(), setValueC() are called that one and then call build and verify results
myXML.build(); // for instance
assertEquals("something you return as Answer", myXML.getValueA());
}
}
The resource https://static.javadoc.io/org.mockito/mockito-core/2.8.9/org/mockito/Mockito.html#stubbing_with_exceptions might be useful - it describes how to stub void methods call.
I try to create test for my presenter, but when I run it, I got this kind of error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
ScalarSynchronousObservable cannot be returned by getContext()
getContext() should return Context
I create my presenter test class like this
public class CreateTalkPresenterTest {
#Mock
TalkService talkService;
#Mock
CreateTalkMvpView createTalkMvpView;
CreateTalkPresenter createTalkPresenter;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
talkService = ServiceFactory.createMapi(createTalkMvpView.getContext(), TalkService.class);
createTalkPresenter = new CreateTalkPresenter(Schedulers.immediate(), Schedulers.immediate());
createTalkPresenter.attachView(createTalkMvpView);
}
#Test
public void createTalkSuccessfullTest() {
TalkService.TalkResultModel talkResultModel = MockModel.newTalkResultModel();
when(talkService.createNewTalk(
FileUtil.createPartFromString("Lorem Ipsum dolor"),
FileUtil.createPartFromString("100"),
null,
FileUtil.createPartFromString("0")
)).thenReturn(Observable.just(talkResultModel));
createTalkPresenter.callCreateTalk("Lorem Ipsum dolor", "100", null);
verify(createTalkMvpView).showProgressIndeterminate();
verify(createTalkMvpView).hideProgressIndeterminate();
verify(createTalkMvpView).showTalkCreated(talkResultModel.object);
}
}
and for Mock the result I use this class
public class MockModel {
public static TalkService.TalkResultModel newTalkResultModel(){
TalkService.TalkResultModel talkResultModel = new TalkService.TalkResultModel();
talkResultModel.code = 600;
talkResultModel.message = "Successfully executed!";
talkResultModel.object = newTalkModel();
return talkResultModel;
}
public static TalkModel newTalkModel(){
Random random = new Random();
String index = String.valueOf(random.nextInt(100));
TalkModel talkModel = new TalkModel();
talkModel.id = index;
talkModel.content = "Lorem Ipsum dolor";
talkModel.categorytalk = newCategoryTalkModel("Category "+index);
talkModel.creator = newConsumerModel("User "+index);
return talkModel;
}
public static CategoryTalkModel newCategoryTalkModel(String name){
CategoryTalkModel categoryTalkModel = new CategoryTalkModel();
categoryTalkModel.id = "100";
categoryTalkModel.name = name;
return categoryTalkModel;
}
public static ConsumerModel newConsumerModel(String name){
Random random = new Random();
String index = String.valueOf(random.nextInt(100));
ConsumerModel consumerModel = new ConsumerModel();
consumerModel.id = index;
consumerModel.username = name;
consumerModel.email = name+"#domain.com";
consumerModel.fullName = "Fullname "+name;
return consumerModel;
}
}
And this is the presenter class that I want to test
public class CreateTalkPresenter implements Presenter<CreateTalkMvpView> {
private CreateTalkMvpView createTalkMvpView;
private TalkService mApiTalkService;
private TalkService.TalkResultModel talkResultModel;
private final Scheduler mainScheduler, ioScheduler;
private Subscription subscription;
public CreateTalkPresenter(Scheduler ioScheduler, Scheduler mainScheduler) {
this.ioScheduler = ioScheduler;
this.mainScheduler = mainScheduler;
}
#Override
public void attachView(CreateTalkMvpView view) {
createTalkMvpView = view;
}
#Override
public void detachView() {
createTalkMvpView = null;
unsubscribe();
}
private void unsubscribe() {
if (subscription != null) subscription.unsubscribe();
}
public void callCreateTalk(String content, String categoryId, String filePath) {
mApiTalkService = ServiceFactory.createMapi(createTalkMvpView.getContext(), TalkService.class);
unsubscribe();
createTalkMvpView.showProgressIndeterminate();
subscription = mApiTalkService.createNewTalk(
FileUtil.createPartFromString(content),
FileUtil.createPartFromString(categoryId),
filePath != null ? FileUtil.prepareFilePart("picture", new File(filePath)) : null,
FileUtil.createPartFromString("0"))
.observeOn(mainScheduler)
.subscribeOn(ioScheduler)
.subscribe(new Subscriber<TalkService.TalkResultModel>() {
#Override
public void onCompleted() {
createTalkMvpView.hideProgressIndeterminate();
createTalkMvpView.showTalkCreated(talkResultModel.object);
}
#Override
public void onError(Throwable e) {
createTalkMvpView.hideProgressIndeterminate();
WarningUtil.onApiError(createTalkMvpView.getContext(), 0, e.getMessage());
}
#Override
public void onNext(TalkService.TalkResultModel talkResultModel) {
CreateTalkPresenter.this.talkResultModel = talkResultModel;
}
});
}
}
I'm using retrofit 2.1.0 and rx android in this case.
So if someone have any idea, what I'm doing wrong in my code. Please help me
Thanks.
talkService isn't a mock. Even though you have this set:
#Mock
TalkService talkService;
You then overwrite it in your #Before method setUp:
talkService = ServiceFactory.createMapi(createTalkMvpView.getContext(), TalkService.class);
So in your test, this happens to a real TalkService implementation:
when(talkService.createNewTalk(/* ... */
)).thenReturn(Observable.just(talkResultModel));
Which then calls a real createNewTalk method, which starts with this:
mApiTalkService = ServiceFactory.createMapi(
createTalkMvpView.getContext(), TalkService.class);
The rest of the method isn't important, because Mockito's when works by mocking the last method that was called before/within the call to when, and nothing else in that method interacts with mocks. If talkService were a mock, then when(talkService.createNewTalk(/*...*/)) would stub the method createNewTalk, but instead it stubs that last mock method call getContext. This makes it look like:
when(createTalkMvpView.getContext()).thenReturn(Observable.just(talkResultModel));
...which exactly matches your error message:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
ScalarSynchronousObservable cannot be returned by getContext()
To fix this, just remove your talkService assignment so the when method call is actually a mock, or use a real talkService as you've initialized it and remove the #Mock annotation and when and verify statements.
I'm getting a very strange error with Mockito:
java.lang.IllegalStateException
at java.util.LinkedList$ListItr.remove(LinkedList.java:923)
at org.mockito.internal.debugging.WarningsFinder.find(WarningsFinder.java:36)
at org.mockito.internal.debugging.WarningsPrinterImpl.print(WarningsPrinterImpl.java:28)
at org.mockito.internal.debugging.WarningsCollector.getWarnings(WarningsCollector.java:34)
at org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:29) <26 internal calls>
There is no reference to my test code, but here are the classes I'm using and the test:
Endpoint.java: Uses Retrofit 2.0
public interface Endpoint {
#GET("items/{itemId}")
Call<List<Item>> list(#Path("itemId") String itemId);
Call<List<Item>> list(#Path("itemId") String itemId, #Path("alternativeId") String alternativeId);
}
Query.java
public class Query {
private String itemId;
// constructor, etc
public String getItemId() {
return itemId;
}
}
ItemDataSource.java: Unfinished implementation (following TDD)
public class ItemDataSource {
private Endpoint endpoint;
// constructor, etc
public Optional<Item> get(Query query) {
List<Item> list = null;
try {
list = endpoint.list(query.getItemId()).execute().body();
} catch (IOException e) {
e.printStackTrace();
}
Template result = modelAdapter.apply(list.get(0));
return Optional.ofNullable(result);
}
}
ItemDataSourceTest.java:
public class TemplateNetworkDataSourceTest {
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Mock
private Query query;
#Mock
private Item item;
#Mock(answer = RETURNS_DEEP_STUBS)
private ItemEndpoint endpoint;
#Test
public void shouldProvideItemFromEndpoint() throws Exception {
when(query.getItemId()).thenReturn("itemId");
when(endpoint.list("itemId").execute()).thenReturn(Response.success(singletonList(networkTemplate)));
ItemDataSource dataSource = new ItemDataSource(endpoint);
Optional<Item> actual = dataSource.get(query);
assertThat(actual.get()).isEqualTo(item);
}
#Test
public void shouldProvideNoItemsWhenNotFound() throws Exception {
//when(query.getProductId()).thenReturn("incorrect_productId"); // <- works
when(endpoint.list(anyString()).execute()).thenReturn(Response.success(emptyList()));
ItemDataSource dataSource = new ItemDataSource(endpoint);
Optional<Item> actual = dataSource.get(query);
assertThat(actual.isPresent()).isFalse();
}
}
If I run it, the first test runs correctly but the second gives me the provided error. If I comment out the first line in the statement of the second test it works (so that could be the answer) but I'm wondering if I'm missing something or if this is a bug on Mockito.
From my slight digging through Mockito's code I see that a List is modified when collecting the warnings after the test is executed.