如何使用PowerMock帮助做TDD?

在我们的日常工作,我们可能会在一些大型的遗留系统上重构或者新添加一些功能;为了不让代码变酸臭,我们会不停的对遗留的代码做重构,对新写的代码使用TDD(测试驱动 开发);但是对于一些大型的,旧的掉渣的系统,其里面包含了各种各样的方法,有静态的,有私有的,有final修饰的,这些方法往往会被很多其他的方法引用到,特别是静态的方法,简直就是全局作用域;或者对于一些被测试的类,其里面包含了一些私有方法或者用final修饰的方法,咱们压根不可能通过集成其类来覆写他们。这个时候我们概怎么办?幸好有PowerMock这个框架。

首先把其Maven的依赖分享出来,然后在一个个的例子来演示,PowerMock的最新版本是1.6.6;所以下面的powermock.version的值是1.6.6,且PowerMock是基于Mockito封装的(也可以基于EasyMock封装,本例子演示的都是基于Mockito封装的。)

        
		1.6.6
	
	
		
			org.powermock
			powermock-module-junit4
			${powermock.version}
			test
		
		
			org.powermock
			powermock-module-testng
			${powermock.version}
			test
		
		
			org.powermock
			powermock-api-mockito
			${powermock.version}
			test
		
		
			junit
			junit
			4.11
		
	

@ 静态方法(Static)

public class IdGenerator {


  /**
   * @return A new ID based on the current time.
   */
  public static long generateNewId() {
     return System.currentTimeMillis();
  }
}

import java.util.HashMap;
import java.util.Map;

public class ServiceRegistartor {
  private Map serviceRegistrations=new HashMap();
  public long registerService(Object service) {
    final long id = IdGenerator.generateNewId();
    serviceRegistrations.put(id, service);
    return id;
 }
}

测试方法:

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;


/**
 * An example on how to mock the call to a static method.
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(IdGenerator.class)
public class ServiceRegistratorTest {

    @Test
    public void registersServiceToRepository() throws Exception {
        long expectedId = 42;
        ServiceRegistartor tested = new ServiceRegistartor();
        mockStatic(IdGenerator.class);
        when(IdGenerator.generateNewId()).thenReturn(expectedId);
        long actualId = tested.registerService(new Object());
        verify(IdGenerator.class);
        assertThat(actualId, is(expectedId));
    }
    
}


注意,在测试方法的上面一定要加上@RunWith(PowerMockRunner.class),@PrepareForTest(IdGenerator.class) 否则会运行失败。


@ 私有方法(Private)

public class CompanyService {

  public final void finalAweSomePay(){
    
  }
  
  public static String getCompany(){
    return CompanyService.getCurrentCompany();
  }
  private static String getCurrentCompany(){  
    return "Objectiva";  
}  
}


测试方法

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(CompanyService.class)
public class CompanyServiceTest {

  @Test
  public void testGetCompany() {
    //PowerMockito.mockStatic(CompanyService.class);
    try {
      PowerMockito.spy(CompanyService.class); //We must add this line,otherwise, the below will invoke the mock method
      //PowerMockito.doReturn("IBM").when(CompanyService.class, "getCurrentCompany"); // Mock私有方法
      PowerMockito.when(CompanyService.class, "getCurrentCompany").thenReturn("IBM");
      System.out.println(CompanyService.getCompany());
      assertEquals(CompanyService.getCompany(),"IBM");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

@ final类或者方法(final)

public class MyBean {
  FinalClass finalClass=null;
  public MyBean(FinalClass finalClass){
    this.finalClass=finalClass;
  }

  public String sayHello(){
      return finalClass.sayHello();
  }
}

public final class FinalClass {

  public  final String sayHello(){
      return "Hello, man!";
  }
}

测试类

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;

/**
 *
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class FinalClassTest {
    @Test
    public void testSayHello() {
        FinalClass finalClass=PowerMockito.mock(FinalClass.class);
        final String  value = "What's up?";
        Mockito.when(finalClass.sayHello()).thenReturn(value);
        
        MyBean myBean=new MyBean(finalClass);
        assertEquals(myBean.sayHello(),value);
        
        Mockito.verify(finalClass, Mockito.times(1)).sayHello();

    }

}

@ 构造函数或者依赖的新类(Constructor)

/**
 * Class to demonstrate how to make constructor testable by Mockito
 */
public class CarFactory {
	/**
	 * Tiny method that wraps constructor. Is partially mocked by test.
	 * 
	 * @param type
	 *            type of the car
	 * @param color
	 *            color of the car
	 * @return created car
	 */
	Car carFactoryMethod(String type, String color) {
		return new Car(type, color);
	}

	/**
	 * Method to demonstrate how to make constructor testable by Mockito
	 * 
	 * @param type
	 *            type of the car
	 * @param color
	 *            color of the car
	 * @return created instance by constructor
	 */
	public Car constructCar(String type, String color) {
		carFactoryMethod(type, color);
		return carFactoryMethod(type, color);
	}
}

/**
 * Used to demonstrate mocking of constructor
 */
public class Car {
	/**
	 * Used for constructor mocking demostration
	 * 
	 * @param type
	 *            type of the car
	 * @param color
	 *            color of the car
	 */
	public Car(String type, String color) {
		throw new UnsupportedOperationException("Fail if not mocked! [type="
				+ type + ", color=" + color + "]");
	}
}
测试类

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;


@RunWith(PowerMockRunner.class)
@PrepareForTest(CarFactory.class)
public class CarFactoryTest {
  private static final String TESTING_TYPE = "Tatra";
  private static final String TESTING_COLOR = "Black";
  @Test
  public void testConstruct() throws Exception{
    Car expectedCar = Mockito.mock(Car.class);
    PowerMockito.whenNew(Car.class).withArguments(TESTING_TYPE, TESTING_COLOR).thenReturn(expectedCar);
    
    CarFactory carFactory = new CarFactory();
    Car actualCar = carFactory.constructCar(TESTING_TYPE, TESTING_COLOR);
    Assert.assertEquals(actualCar, expectedCar);
    PowerMockito.verifyNew(Car.class, Mockito.times(2)).withArguments(TESTING_TYPE, TESTING_COLOR);
  }
}

注意:@PrepareForTest(CarFactory.class)里必须是CarFactory.class类,而不是Car.class类。

@参考资料:

https://github.com/powermock

https://github.com/powermock/powermock.github.io

https://blog.jayway.com/2013/03/05/beyond-mocking-with-powermock/

https://github.com/powermock/powermock/wiki/GettingStarted

https://github.com/powermock/powermock/wiki/MockitoUsage

https://github.com/lkrnac/blog-2014-01-unusual-mocking


你可能感兴趣的:(编程实践,TDD&BDD)