天天看点

问题记录本1--单元测试PowerMockito @Before与@BeforeClass问题记录本1–单元测试PowerMockito @Before与@BeforeClass

问题记录本1–单元测试PowerMockito @Before与@BeforeClass

最近写单元测试的时候遇到一个问题,记录如下:

被测试类:

public class Car() {
	private final static LogClass LOGGER = LogFactory.getLog(Car.class);
	public boolean drive() {
		// 打开车门
		String doorResult = Door.getInstance().open();
		try {
			if (doorResult != null && doorResult.isNotEmpty) {
				// 发动汽车
				return startCar();
			}
		} catch (Exception ex) {
			LOGGER.error("汽车发动异常", ex);
		}
		return false;
	}

	public boolean startCar() {
		...
	}
}
           

Car是现在需要被测试的类,其中该类中有一个静态变量LOGGER,drive方法中构建了一个单例Door,那么如果要写单元测试,需要模拟startCar抛出异常的case,那就要将静态变量和单例mock出来。一开始我的单测是这么写的:

@RunWith(PowerMockRunner.class)
@PrepareForTest({
        Car.class,
        Door.class
})
public class CarTest {
	@InjectMocks
	private Car car;

	private Door door;
	private LogClass logger;

	@Before
	public void setUp() {
		PowerMockito.mockStatic(Door.class);
		PowerMockito.mockStatic(LogFactory.class);
		
		door = PowerMockito.mock(Door.class);
		PowerMockito.when(Door.getInstance()).thenReturn(door);

		logger = PowerMockito.mock(LogClass.class);
		PowerMockito.when(LogFactory.getLog(Car.class)).thenReturn(logger);
	}
	
	@Test
	public void driveTest1() {
		PowerMockito.when(door.open).thenReturn("Success");
		Exception ex = new JSONException("a exception");
		PowerMockito.when(car.startCar()).thenThrow(ex);
		boolean result = car.drive();
		Mockito.verify(logger).error("汽车发动异常", ex);
	}
	
	@Test
	public void driveTest2() {
		PowerMockito.when(door.open).thenReturn("Success");
		PowerMockito.when(car.startCar()).thenReturn(true);
		boolean result = car.drive();
		Assert.assertTrue(result);
	}
}
           

我的本意是想要把日志工厂LogFactory给mock出来,然后再进行异常抛出的时候就能够验证这个异常。

但是在实际执行这个单测时,logger并不是我Mock出来的那一个,而是在被测类中构建的。

这是因为被测类中的LOGGER 是一个静态变量,会在类加载的时候就被构建出来,接着代码才会走到单测setUp方法中,此时再去构建一个mock的logger是没有用的。

于是将单测类中的 @Before 改成了 @BeforeClass,这是为了在类加载之前先执行我的mock步骤,改后的代码如下:

@RunWith(PowerMockRunner.class)
@PrepareForTest({
        Car.class,
        Door.class
})
public class CarTest {
	@InjectMocks
	private Car car;

	private static Door door;
	private static LogClass logger;

	@BeforeClass
	public static void setUp() {
		PowerMockito.mockStatic(Door.class);
		PowerMockito.mockStatic(LogFactory.class);
		
		door = PowerMockito.mock(Door.class);
		PowerMockito.when(Door.getInstance()).thenReturn(door);

		logger = PowerMockito.mock(LogClass.class);
		PowerMockito.when(LogFactory.getLog(Car.class)).thenReturn(logger);
	}
	
	@Test
	public void driveTest1() {
		PowerMockito.when(door.open).thenReturn("Success");
		Exception ex = new JSONException("a exception");
		PowerMockito.when(car.startCar()).thenThrow(ex);
		boolean result = car.drive();
		Mockito.verify(logger).error("汽车发动异常", ex);
	}
	
	@Test
	public void driveTest2() {
		PowerMockito.when(door.open).thenReturn("Success");
		PowerMockito.when(car.startCar()).thenReturn(true);
		boolean result = car.drive();
		Assert.assertTrue(result);
	}
}
           

此时解决了上述logger无法mock的问题,然而新的问题又出来了,因为我写了一个driveTest2方法,在单独执行的时候,Door确实是我mock出来的一个对象,但是在执行整个Test类的时候,发现driveTest2中的Door又是在代码中构建出来的对象了(之所以会发现,是因为Door在构建的时候会有一些日志打印在控制台中,如果是mock出来的就完全不会)。

为什么在单测修改之前不会出现这个问题呢?

为什么在单独执行每个单测方法的时候不会出现这个问题呢?

于是我想到了会不会是改成了@BeforeClass导致的,查阅了一些资料,发现果真如此

@BeforeClass在类中只会执行一次,而@Before会在每个方法执行之前都执行一次

接着我将代码修改如下,让 @BeforeClass 和 @Before 各司其职,完美解决问题。

@RunWith(PowerMockRunner.class)
@PrepareForTest({
        Car.class,
        Door.class
})
public class CarTest {
	@InjectMocks
	private Car car;

	private Door door;
	private static LogClass logger;

	@BeforeClass
	public static void setUp() {
		PowerMockito.mockStatic(LogFactory.class);
		logger = PowerMockito.mock(LogClass.class);
		PowerMockito.when(LogFactory.getLog(Car.class)).thenReturn(logger);
	}

	@Before
	public void setUp2() {
		PowerMockito.mockStatic(Door.class);
		door = PowerMockito.mock(Door.class);
		PowerMockito.when(Door.getInstance()).thenReturn(door);
	}
	
	@Test
	public void driveTest1() {
		PowerMockito.when(door.open).thenReturn("Success");
		Exception ex = new JSONException("a exception");
		PowerMockito.when(car.startCar()).thenThrow(ex);
		boolean result = car.drive();
		Mockito.verify(logger).error("汽车发动异常", ex);
	}
	
	@Test
	public void driveTest2() {
		PowerMockito.when(door.open).thenReturn("Success");
		PowerMockito.when(car.startCar()).thenReturn(true);
		boolean result = car.drive();
		Assert.assertTrue(result);
	}
}
           

继续阅读