9

the below posted method in the code section contains a static method which is "with()". I want to test the code in below, so I coded the test of this method as shown in the testing section.

i tried to test the method using both of "spy()" and "mock()" but the test fails alwyas.

please let me know how can I test a method returns void?

code

 public RequestCreator requestCreatorFromUrl(String picUrl) {
    return Picasso.with(mCtx).load(picUrl);
}

testing:

public class ValidationTest {
@Mock
private Context mCtx = null;
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();

@Before
public void setUp() throws Exception {
    mCtx = Mockito.mock(Context.class);
    Assert.assertNotNull("Context is not null", mCtx);
}

 @Test
public void whenRequestCreatorFromUrlTest() throws Exception {
    Picasso picasso = Picasso.with(mCtx);
    Picasso spyPicasso = spy(picasso);
    Uri mockUri = mock(Uri.class);
    RequestCreator requestCreator = Picasso.with(mCtx).load(mockUri);
    RequestCreator spyRequestCreator = spy(requestCreator);

    doReturn(spyRequestCreator).when(spyPicasso.load(mockUri));
    //when(spyPicasso.load(mockUri)).thenReturn(spyRequestCreator);
    RequestCreator actual = spyPicasso.load(mockUri);

    Assert.assertEquals(requestCreator, actual);
}
Hemen Ashodia
  • 499
  • 3
  • 16
Amrmsmb
  • 1
  • 27
  • 104
  • 226
  • 1
    And of course: the default answer is - if possible change your production code to **not** have that need to mock static methods. – GhostCat Sep 18 '17 at 10:37
  • @GhostCat, Agree, avoid as much as possible the need to mock the static methods. But _Letsamrit_ wrote in comments below, that _because Picasso class is from an external library I am using_. I think in such situation it is a necessary evil to mock the static stuff. – mxf Sep 18 '17 at 12:44
  • 1
    @mxf Depends on how much effort you are willing to invest. You could *always* create your own *interface* wrapping around such functionality. And then you provide a small impl class that just calls the static stuff. That also helps you in **de coupling** your logic from that external library. – GhostCat Sep 18 '17 at 12:49
  • @GhostCat, thanks, good solution. – mxf Sep 18 '17 at 13:22
  • @mxf Sure ;-) ... and most likely, you would find one of my answers ... already saying exactly that somewhere. – GhostCat Sep 18 '17 at 14:03

4 Answers4

9

Usually, if you end up using PowerMock, that’s a good sign that you most possibly are on the wrong way.

What if instead of directly referring to Picasso, you create a component, whose responsibility will be to load an image, let's say class ImageLoader. What will this give to you?

  • Separation of concerns: if tomorrow you decide to move to Glide, you shouldn't change each and every class where you were using Picasso, you will just change implementation of ImageLoader. Other components are non-wiser of these changes, because they are dependent on an abstraction, not on implementation

  • Seam: this will allow you easily mock dependencies in order to perform unit testing

This will be our abstraction:



    interface ImageLoader {
        RequestCreator load(String url);
    }


Let’s provide an implementation:



    class ImageLoaderImpl implements ImageLoader {
        
        private final Picasso picasso;
    
        public ImageLoaderImpl(Context context) {
            this.picasso = Picasso.with(context);
        }
    
        @Override
        public RequestCreator load(String url) {
            return picasso.load(url);
        }
    }


Now, in your components whenever you need Picasso use ImageLoader instead.

Thus, your method becomes following:



    public static RequestCreator requestCreatorFromUrl(String picUrl) {
        return imageLoader.load(picUrl);
    }


Then your test will look like this:



    @Test
    public void test() {
        ImageLoaderImpl imageLoader = Mockito.mock(ImageLoaderImpl.class);
        RequestCreator expected = Mockito.mock(RequestCreator.class);
        String TEST_URL = "https://www.some.url/img.jpg";
    
        when(imageLoader.load(TEST_URL)).thenReturn(expexted);
    
        RequestCreator actual = clazzToTest.requestCreatorFromUrl(TEST_URL);
    
        assertEquals(expected, actual);
    }


No mocking of static method, no PowerMock needed.

laim2003
  • 299
  • 4
  • 19
azizbekian
  • 60,783
  • 13
  • 169
  • 249
3

From the Mockito's FAQ:

What are the limitations of Mockito

...

Cannot mock static methods

Use PowerMock instead. Here you will find the detailed instruction about how to mock the static methods.

Update

In order to apply the PowerMock to your test you need to:

  • Add @PrepareForTest at test class level:

    @PrepareForTest(Picasso.class) public class ValidationTest { ... }

  • Call PowerMockito.mockStatic() to mock a static class

    PowerMockito.mockStatic(Picasso.class);

  • Just use Mockito.when() to setup your expectation:

    Mockito.when(Picasso.with(mCtx)).thenReturn(requestCreator);

The same set of steps is applicable for the RequestCreator.class.

P.S. I can make mistakes because I do not know the API of 3rd party library you use.

mxf
  • 920
  • 6
  • 8
  • One can use **PowerMockito**. Although of course, that means using outdated versions of Mockito. – GhostCat Sep 18 '17 at 10:37
  • can you please provide a hint how to use powermockito to test my method?? because Picasso class is from an external library I am using..i do not have the code of it, i just added the jar to gradle.build – Amrmsmb Sep 18 '17 at 10:42
  • @GhostCat, I've never heard about the **PowerMockito** framework. Did you mean [org.powermock.api.mockito.PowerMockito](http://static.javadoc.io/org.powermock/powermock-api-mockito/1.6.4/org/powermock/api/mockito/PowerMockito.html)? – mxf Sep 18 '17 at 12:29
  • Yes, but a better link would be: https://github.com/powermock/powermock/wiki/Mockito – GhostCat Sep 18 '17 at 12:35
  • @mxf i need your help please. i did the following but i have a problem with RequestCreator because Picasso.with(mCtx) returns RequestCreator..how should I test .load method? my code:@Test public void whenRequestCreatorFromUrlTest() throws Exception { PowerMockito.mockStatic(Picasso.class); Uri mockUri = mock(Uri.class); Mockito.when(Picasso.with(mCtx).load(mockUri)).thenReturn(RequestCreator.class) } – Amrmsmb Sep 18 '17 at 13:02
  • @LetsamrIt, split "Mockito.when(Picasso.with(mCtx).load(mockUri)).thenReturn(Re‌​questCreator.class) }" in two separate mock creation - one for `Mockito.when(Picasso.with(mCtx)).then...` and the second one for `Mockito.when(....load(mockUri)).then...`. – mxf Sep 18 '17 at 13:21
  • for the 1st split, i do the following and there is error for Picasso.class: PowerMockito.mockStatic(Picasso.class); Uri mockUri = mock(Uri.class); Mockito.when(Picasso.with(mCtx)).thenReturn(Picasso.class); but for the snd split, i receive always errors..i would appreciate it if you help me – Amrmsmb Sep 18 '17 at 13:40
0

Forget PowerMockito. With mockito-inline version > 4.2.0 you can use it by @Mock annotation. I haven't test your code, but you can have some insights with this answer.

You can check the use in my conclusion work of university (Github).

class Test {
    @Mock
    MockedStatic<Picasso> picasso;

    @BeforeEach
    public void beforeEach() {
       doNothing().when(Picasso..with(any()));
    }
}

There's another approach that consist to mock static manually, which I don't prefer, but work fine. You can find the example in Baeldung website

0
try (MockedStatic<SOMECLASS> instantMockedStatic = Mockito.mockStatic(SOMECLASS.class, Mockito.CALLS_REAL_METHODS)) {
            instantMockedStatic.when(SOME_CLASS::SOME_STATIC_METHOD).thenReturn(startValue);
   // some validation logic here, including methods that call real class...
}

e.g.

final Instant startValue = Instant.ofEpochSecond(0);
try (MockedStatic<Instant> instantMockedStatic = Mockito.mockStatic(Instant.class, Mockito.CALLS_REAL_METHODS)) {
    instantMockedStatic.when(Instant::now).thenReturn(startValue);

    Assertions.assertEquals(startValue.plus(Duration.ofSeconds(1)) , somethingThatUsesInstantNowAndAdds1Sec());
Nadav
  • 1,167
  • 2
  • 19
  • 43