Java单元测试用例的编写,有什么技巧?(一)

发表于:2021-5-07 09:29

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:阿里技术    来源:知乎

分享:
  编写Java单元测试用例,其实就是把“复杂的问题要简单化”——即把一段复杂的代码拆解成一系列简单的单元测试用例;写好Java单元测试用例,其实就是把“简单的问题要深入化”——即学习一套方法、总结一套模式并应用到实践中。这里,作者根据日常的工作经验,总结了一些Java单元测试技巧,以供大家交流和学习。
  1. 准备环境
  PowerMock是一个扩展了其它如EasyMock等mock框架的、功能更加强大的框架。PowerMock使用一个自定义类加载器和字节码操作来模拟静态方法、构造方法、final类和方法、私有方法、去除静态初始化器等等。
  1.1. 引入PowerMock包
  为了引入PowerMock包,需要在pom.xml文件中加入下列maven依赖:
  1.2. 集成SpringMVC项目
  在SpringMVC项目中,需要在pom.xml文件中加入JUnit的maven依赖:
  1.3. 集成SpringBoot项目
  在SpringBoot项目中,需要在pom.xml文件中加入JUnit的maven依赖:
  1.4. 一个简单的测试用例
  这里,用List举例,模拟一个不存在的列表,但是返回的列表大小为100。
  public class ListTest {
      @Test
      public void testSize() {
          Integer expected = 100;
          List list = PowerMockito.mock(List.class);
          PowerMockito.when(list.size()).thenReturn(expected);
          Integer actual = list.size();
          Assert.assertEquals("返回值不相等", expected, actual);
      }
  }
  2. mock语句
  2.1. mock方法
  声明:
  T PowerMockito.mock(Class clazz);
  用途:
  可以用于模拟指定类的对象实例。
  当模拟非final类(接口、普通类、虚基类)的非final方法时,不必使用@RunWith和@PrepareForTest注解。当模拟final类或final方法时,必须使用@RunWith和@PrepareForTest注解。注解形如:
  @RunWith(PowerMockRunner.class)
  @PrepareForTest({TargetClass.class})
  2.1.1. 模拟非final类普通方法
  @Getter
  @Setter
  @ToString
  public class Rectangle implements Sharp {
      private double width;
      private double height;
      @Override
      public double getArea() {
          return width * height;
      }
  }
  public class RectangleTest {
      @Test
      public void testGetArea() {
          double expectArea = 100.0D;
          Rectangle rectangle = PowerMockito.mock(Rectangle.class);
          PowerMockito.when(rectangle.getArea()).thenReturn(expectArea);
          double actualArea = rectangle.getArea();
          Assert.assertEquals("返回值不相等", expectArea, actualArea, 1E-6D);
      }
  }
  2.1.2. 模拟final类或final方法
  @Getter
  @Setter
  @ToString
  public final class Circle {
      private double radius;
      public double getArea() {
          return Math.PI * Math.pow(radius, 2);
      }
  }
  @RunWith(PowerMockRunner.class)
  @PrepareForTest({Circle.class})
  public class CircleTest {
      @Test
      public void testGetArea() {
          double expectArea = 3.14D;
          Circle circle = PowerMockito.mock(Circle.class);
          PowerMockito.when(circle.getArea()).thenReturn(expectArea);
          double actualArea = circle.getArea();
          Assert.assertEquals("返回值不相等", expectArea, actualArea, 1E-6D);
      }
  }
  2.2. mockStatic方法
  声明:
  PowerMockito.mockStatic(Class clazz);
  用途:
  可以用于模拟类的静态方法,必须使用“@RunWith”和“@PrepareForTest”注解。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest({StringUtils.class})
  public class StringUtilsTest {
      @Test
      public void testIsEmpty() {
          String string = "abc";
          boolean expected = true;
          PowerMockito.mockStatic(StringUtils.class);
          PowerMockito.when(StringUtils.isEmpty(string)).thenReturn(expected);
          boolean actual = StringUtils.isEmpty(string);
          Assert.assertEquals("返回值不相等", expected, actual);
      }
  }
  3. spy语句
  如果一个对象,我们只希望模拟它的部分方法,而希望其它方法跟原来一样,可以使用PowerMockito.spy方法代替PowerMockito.mock方法。于是,通过when语句设置过的方法,调用的是模拟方法;而没有通过when语句设置的方法,调用的是原有方法。
  3.1. spy类
  声明:
  PowerMockito.spy(Class clazz);
  用途:
  用于模拟类的部分方法。
  案例:
  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;
      }
  }
  @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);
      }
  }
  3.2. spy对象
  声明:
  T PowerMockito.spy(T object);
  用途:
  用于模拟对象的部分方法。
  案例:
  public class UserService {
      private Long superUserId;
      public boolean isNotSuperUser(Long userId) {
          return !isSuperUser(userId);
      }
      public boolean isSuperUser(Long userId) {
          return Objects.equals(userId, superUserId);
      }
  }
  @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);
      }
  }

      本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
51Testing“十佳作者”计划,投稿不只有稿费!

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计

法律顾问:上海漕溪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2022
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号