Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test a static method using mock() and spy()

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);
}
like image 477
LetsamrIt Avatar asked Sep 18 '17 10:09

LetsamrIt


People also ask

Can we use spy for static methods?

With Spy in Mockito, we can eliminate the additional code ItemServiceForTest introduced previously in our test. However, we still need to refactor the production code to override the static method.

How do you test static methods?

There are three ways to test the code that calls static methods: Create a wrapper class and use dependency injection. Use a static Func property. Use the Extract and override call.

Can I mock static methods?

Since static method belongs to the class, there is no way in Mockito to mock static methods. However, we can use PowerMock along with Mockito framework to mock static methods.

Can we use spy with mock?

Spies are useful when we have a huge class full of methods, and we want to mock certain methods. In this scenario, we should prefer using spies rather than mocks and stubs. It calls the real method behavior, if the methods are not stubbed. In Mockito, spy() method is used for creating spy objects.


2 Answers

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.

like image 103
azizbekian Avatar answered Oct 11 '22 15:10

azizbekian


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.

like image 23
mxf Avatar answered Oct 11 '22 16:10

mxf