[フレーム]
Last Updated: February 25, 2016
·
2.849K
· juliomistral

PowerMock - Completing your unit testing toolkit

Let's face it: Mocking/Stubbing are so integral to proper unit testing, that you really can't unit test without it. But the standard libraries - EasyMock, Mockito, JMock, etc. - don't support mocking/stubbing the following uses cases out of the box:

  • Static method calls (MyStaticClass.someStaticMethod)
  • Constructors (new MyClass())
  • Verify that a private method was called

Now, when ever you look this up you'll see a lot of responses along the lines of "if you need to do this, you're design is wrong...and you're a jerk!". Well, most time this is true, but the fact of the matter is you can have a really well design application BUT you are still using static methods and constructors. You can't use a factory in EVERY case, and besides: how would you properly unit test a factory unless you can verify what it constructs AND how it constructs it.

So one of the best things I ever found for unit testing in Java is PowerMock. By using it in conjunction with EasyMock or Mockito, you open up your mocking/stubbing toolset to include the aforementioned cases. Here's a unit test I wrote as part of a pet project I'm starting up, a web resource crawler and reporter called charlotte.

Take a look, learn it AND use it! May the unit tests be with you...


package com.hcisf.charlotte.loader;


import com.hcisf.PowerMockWithSpecTestNameRunner;

import com.hcisf.charlotte.domain.Resource;
import com.hcisf.charlotte.domain.ResourceStatus;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import org.powermock.api.mockito.PowerMockito;
import static org.mockito.Mockito.*;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;


@RunWith(PowerMockWithSpecTestNameRunner.class)
@PrepareForTest({Jsoup.class, JsoupHttpLoader.class})
public class JsoupHttpLoaderTest {
 private static final int TIMEOUT = 1000;
 private static final String RESOURCE_URL = "http://google.com";

 JsoupHttpLoader loader;
 Resource resource;
 Document document;
 Elements elements;

 @Before
 public void setup() throws Exception {
 PowerMockito.mockStatic(Jsoup.class);

 resource = new Resource(RESOURCE_URL);
 document = mock(Document.class);
 elements = new Elements();
 doReturn(elements).when(document).select(anyString());

 when(
 Jsoup.parse(any(URL.class), anyInt())
 ).thenReturn(
 document
 );

 loader = new JsoupHttpLoader(TIMEOUT);
 }

 @Test
 public void shouldAttemptToLoadTheContentAtTheProvidedResourceLocation() throws Exception {
 // When the loader populates the resource
 loader.populateResource(resource);

 // Then parsing library is asked to parse the content at the resource location
 PowerMockito.verifyStatic();
 Jsoup.parse(any(URL.class), eq(TIMEOUT));
 }

 @Test
 public void shouldReturnAResourceSetToParsedIfTheLoadedContentIsParsedSuccessfully() throws Exception {
 // When the loader loads the resource URL
 Resource output;
 output = loader.loadResource(RESOURCE_URL);

 // Then the returned resource is set to parsed
 assertNotNull(output);
 assertEquals(ResourceStatus.PARSED, output.status);
 }

 @Test
 public void shouldReturnAResourceSetToInvalidIfLocationIsMalformed() throws Exception {
 // Given the provided resource location is malformed
 PowerMockito.whenNew(URL.class)
 .withArguments(RESOURCE_URL)
 .thenThrow(new MalformedURLException());

 // When the loader loads the resource URL
 Resource output;
 output = loader.loadResource(RESOURCE_URL);

 // Then the returned resource is set to invalid
 assertNotNull(output);
 assertEquals(ResourceStatus.INVALID, output.status);
 }

 @Test
 public void shouldReturnAResourceSetToUnavailableIfCannotConnectToLocation() throws Exception {
 // Given the provided resource location is unavailable
 PowerMockito.whenNew(URL.class)
 .withArguments(RESOURCE_URL)
 .thenThrow(new IOException());

 // When the loader loads the resource URL
 Resource output;
 output = loader.loadResource(RESOURCE_URL);

 // Then the returned resource is set to unavailable
 assertNotNull(output);
 assertEquals(ResourceStatus.UNAVAILABLE, output.status);
 }

 @Test
 public void shouldCreateAChildResourceForEveryLinkFoundInTheParsedDocument() throws Exception {
 // Given the parsed document contains a link in it
 Element element = mock(Element.class);
 when(
 element.attr(anyString())
 ).thenReturn(
 RESOURCE_URL
 );
 elements.add(element);

 // When the loader loads the resource URL
 Resource output;
 output = loader.loadResource(RESOURCE_URL);

 // Then the returned resource has 1 child
 assertNotNull(output.children);
 assert output.children.size() == 1;
 Resource child = output.children.get(0);

 // Add the child is an empty resource with it's location set
 assertEquals(ResourceStatus.EMPTY, child.status);
 assertEquals(RESOURCE_URL, child.location);
 }
}

AltStyle によって変換されたページ (->オリジナル) /