JAVA JUnit Test

555 阅读4分钟

采用powermock书写java UT.

pom文件引入相关依赖

<!-- PowerMock JUnit 4.4+ Module -->
<dependency>
  <groupId>org.powermock</groupId>
  <artifactId>powermock-module-junit4</artifactId>
  <version>2.0.0</version>
  <scope>test</scope>
</dependency>
<!-- PowerMock Mockito2 API -->
<dependency>
  <groupId>org.powermock</groupId>
  <artifactId>powermock-api-mockito2</artifactId>
  <version>2.0.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>

相关注解说明

// 告诉JUnit使用PowerMockRunner进行测试
@RunWith(PowerMockRunner.class) 

// 所有需要测试的类列在此处,适用于模拟final类或有final, private, static, native方法的类
@PrepareForTest({RandomUtil.class}) 

//忽略的程序包中的类(不加载指定包); 常用于解决使用powermock后,提示classloader错误
@PowerMockIgnore("javax.management.*") 

// 创建一个实例,这个实例可以调用真实代码的方法
@InjectMocks

// 创建了一个全部Mock的实例,所有属性和方法全被置空(0或者null)。
@Mock

mock 返回值

// mock期望值
1. PowerMockito.doReturn(expected).when(mockList).get(index);
2. PowerMockito.when(mockList.get(index)).thenReturn(expected);

// mock异常返回
1. PowerMockito.doThrow(new IndexOutOfBoundsException()).when(mockList).get(index);
2. PowerMockito.when(mockList.get(index)).thenThrow(new IndexOutOfBoundsException());

// 无返回值
PowerMockito.doNothing().when(mockList).clear();

//注意:when().thenReturn()模式会执行原方法,而doReturn().when()模式不会执行原方法。

校验返回

Mockito.verify(mockList).clear(); //验证是否方法被调用

Mockito.verify(mockList, Mockito.times(1)).clear(); //验证方法的调用次数

mock参数

Mockito提供Mockito.anyInt()、Mockito.anyString、Mockito.any(Class clazz)等来表示任意值。

当我们使用参数匹配器时,所有参数都应使用匹配器。 如果要为某一参数指定特定值时,就需要使用Mockito.eq()方法。

实例

对于不同的类或者方法mock的方式。

------MockMapper.class
public interface MockMapper {
  public int count(MockModel model);

}

-----MockServiceImpl.class
@Service
public class MockServiceImpl {
  @Autowired
  private MockMapper mockMapper;

  public int count(MockModel model) {
    return mockMapper.count(model);
  }
    
    public boolean makeFile(String path) {
       File file = new File(path);
       return file.exists();
    }
}

------MockUtil.class
public class MockUtil {
    private static final Random random = new Random();
    
    public static int nextInt(int bound) {
        return random.nextInt(bound);
    }
}

mock普通方法

@RunWith(PowerMockRunner.class) 
@PrepareForTest({MockUtil.class}) 
@PowerMockIgnore("javax.management.*")
public class MockExample {
    // 将@Mock注解的示例注入进来
    @InjectMocks
    private MockServiceImpl mockService;
    @Mock
    private MockMapper mockMapper;
    
    /**
     * mock普通方法
     */
    @Test
    public void testSelectAppAdvertisementList() {
        MockModel model = new MockModel();
        PowerMockito.when(mockMapper.count(model)).thenReturn(2);
        Assert.assertEquals(2, mockService.count(model));
    }
}

Mock静态方法

  1. 声明:
PowerMockito.mockStatic(Class clazz);
PrepareForTest({Class clazz});
  1. 使用
@RunWith(PowerMockRunner.class) 
@PrepareForTest({MockUtil.class}) 
@PowerMockIgnore("javax.management.*")
public class MockStaticExample {
  @Test 
  public void testStaticMethod() { 
    PowerMockito.mockStatic(MockUtil.class); 
    PowerMockito.when(MockUtil.nextInt(10)).thenReturn(7); 
    Assert.assertEquals(7, MockUtil.nextInt(10)); 
  }
}

Mock方法内的构造函数

  1. 声明
  • 传指定参数进行构造
PowerMockito.whenNew(*.class).withArguments(argument1, ...).thenReturn(file)
  • 不管参数进行构造
PowerMockito.whenNew(*.class).withAnyArguments().thenReturn(file)
  1. 使用
public final class FileUtils {

    public static boolean isFile(String fileName) {
        return new File(fileName).isFile();
    }

}

----UT
@RunWith(PowerMockRunner.class)

@PrepareForTest({FileUtils.class})
public class MockStructureExample {
    @Test
    public void testIsFile() throws Exception {
        String fileName = "test.txt";
        File file = PowerMockito.mock(File.class);
        PowerMockito.whenNew(File.class).withArguments(fileName).thenReturn(file);
        PowerMockito.when(file.isFile()).thenReturn(true);
        Assert.assertTrue("返回值为假", FileUtils.isFile(fileName));
    }
}

mock 类的部分方法 spy

如果一个对象,我们只希望模拟它的部分方法,而希望其它方法跟原来一样,可以使用PowerMockito.spy方法代替PowerMockito.mock方法。于是,通过when语句设置过的方法,调用的是模拟方法;而没有通过when语句设置的方法,调用的是原有方法。

  1. 声明:
PowerMockito.spy(Class clazz); // 类
T PowerMockito.spy(T object); //spy对象
  1. 使用 示例1
public class StringUtils {

    public static boolean isNotEmpty(final CharSequence cs) {
        return !isEmpty(cs);
    }
    
    public static boolean isEmpty(final CharSequence cs) {
        return cs == null || cs.length() == 0;
    }
}

----UT
@RunWith(PowerMockRunner.class)
@PrepareForTest({ StringUtils.class })
public class StringUtilsTest {

    @Test
    public void testIsNotEmpty() {
        String string = null;
        boolean expected = true;
        PowerMockito.spy(StringUtils.class);
        PowerMockito.when(StringUtils.isEmpty(string)).thenReturn(!expected);
        boolean actual = StringUtils.isNotEmpty(string);
        Assert.assertEquals("返回值不相等", expected, actual);
    }

}

示例2 spy对象

public class UserService {

    private Long superUserId;

    public boolean isNotSuperUser(Long userId) {
        return !isSuperUser(userId);
    }

    public boolean isSuperUser(Long userId) {
        return Objects.equals(userId, superUserId);
    }
}

---- UT
@RunWith(PowerMockRunner.class)
public class UserServiceTest {

    @Test
    public void testIsNotSuperUser() {
        Long userId = 1L;
        boolean expected = false;
        UserService userService = PowerMockito.spy(new UserService());
        PowerMockito.when(userService.isSuperUser(userId)).thenReturn(!expected);
        boolean actual = userService.isNotSuperUser(userId);
        Assert.assertEquals("返回值不相等", expected, actual);
    }

}

mock系统类

注意:由于URLEncoder类是系统类,因此我们应该准备SystemClassUser进行测试,因为这是调用的encode方法的类URLEncoder。 示例1

public class SystemClassUser {

	public String performEncode() throws UnsupportedEncodingException {
		return URLEncoder.encode("string", "enc");
	}
}

---UT
@RunWith(PowerMockRunner.class)
@PrepareForTest( { SystemClassUser.class })
public class SystemClassUserTest {

	@Test
	public void assertThatMockingOfNonFinalSystemClassesWorks() throws Exception {
		mockStatic(URLEncoder.class);

		expect(URLEncoder.encode("string", "enc")).andReturn("something");
		replayAll();

		assertEquals("something", new SystemClassUser().performEncode());

		verifyAll();
	}
}

示例2

public class AppShell {
  public Process exec(String command) {
    return Runtime.getRuntime().exec(command);
  }
}

----UT
@RunWith(PowerMockRunner.class)
@PrepareForTest(AppShell.class)
public class AppShellTest {

    @Mock private Runtime mockRuntime;

    @Test
    public void test() {
        PowerMockito.mockStatic(Runtime.class);

        when(Runtime.getRuntime()).thenReturn(mockRuntime);
        when(mockRuntime.exec()).thenReturn("whatever you want");

        // do the rest of your test
    }
}

mock类的私有方法、属性

  1. 声明:
  • mock私有属性
// 第一种方式; 原生JUnit进行单元测试时使用
ReflectionTestUtils.setField(obj, fieldName, value);
// 第二种方式;Whitebox.setInternalState powermock时使用
Whitebox.setInternalState(obj, fieldName, value);
  • mock私有方法
1. PowerMockito.stub(PowerMockito.method(UserService.class, "isSuperUser", Long.class)).toReturn(!expected);
2. PowerMockito.when(userService, "isSuperUser", userId).thenReturn(!expected);
  1. 使用
@Service
public class UserService {

    @Value("${system.userLimit}")
    private Long userLimit;

    public Long getUserLimit() {
        return userLimit;
    }
    
    private boolean isSuperUser(Long userId) {
        return Objects.equals(userId, superUserId);
    }

}

-----UT
public class UserServiceTest {

    @Autowired
    private UserService userService;

    // mock 私有属性
    @Test
    public void testGetUserLimit() {
        Long expected = 1000L;
        ReflectionTestUtils.setField(userService, "userLimit", expected);
        // or 下面这一种
        // Whitebox.setInternalState(userService, "userLimit", expected);
        Long actual = userService.getUserLimit();
        Assert.assertEquals("返回值不相等", expected, actual);
    }
    // mock 私有方法
    @Test
    public void testIsNotSuperUser() throws Exception {
        Long userId = 1L;
        boolean expected = false;
        UserService userService = PowerMockito.spy(new UserService());
        PowerMockito.when(userService, "isSuperUser", userId).thenReturn(!expected);
        PowerMockito.stub(PowerMockito.method(UserService.class, "isSuperUser", Long.class)).toReturn(!expected);
        boolean actual = userService.isNotSuperUser(userId);
        Assert.assertEquals("返回值不相等", expected, actual);
    }

}

参考链接

github.com/powermock/p…