Android——单元测试(1)

本文探讨了Android开发中单元测试的必要性,指出它能显著提高开发效率,确保代码逻辑正确。介绍了单元测试的一般思路,包括针对有返回值和无返回值的方法进行测试的方法,以及如何处理耗时操作。同时,文中提供了一个涉及反射机制的测试例子,引导读者了解更多相关内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考1
参考2

单元测试的必要性

在Android开发中,我们常常需要把程序运行起来才能看到一些算法、请求数据或者是逻辑的效果,这样是很费时间的。但是在实际开发中,我们需要不断看到写出的代码是什么效果,如果完全通过运行来检查逻辑是否正确,那么可能有3/5的时间都是花费在等待编译运行上了。
如果我们在看法过程中针对一个逻辑进行单元测试,那么可以很快的看到这段代码逻辑的效果,从而节省大量的时间。在《Clean Code》中,鲍勃大叔通过和许多编程教父的交谈,总结出了“单元测试”是必要的结论。它保证了代码能编译通过,能正确执行我们想要的逻辑。这是写好一个程序或者模块的基础。

单元测试的一般思路

  • 方法有返回值时,采用判断符来进行测试。
  • 没有返回值时,寻找方法执行完成后,有哪些对象属性发生了变化,然后打印/或者判断对象的属性。
  • 对于耗时操作的测试,需要使用CountDownLatch来帮助阻断测试线程,以等待耗时操作完成,获取结果。

例子

下面的例子中运用到了反射机制,请移步我的另一篇文章,查看Java反射机制。
点击此处传送门Java反射机制

"需要测试的MainActivity:"
public class MainActivity extends AppCompatActivity {

  @BindView(R.id.play_anim)
  Button button;
  @BindView(R.id.ll)
  LinearLayout ll;
  @BindView(R.id.stop_game_icon)
  View stopGameIcon;
  @BindView(R.id.display_image)
  ImageView displayImage;

  private ViewWrapper viewWrapper;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    initViewData();
    initData();
    addListener();
  }

  private void addListener() {
    displayImage.setOnClickListener(v -> initData());
  }

  private int i = 0;

  //需要测试的第一个方法,这是一个void返回值的,包括耗时操作的方法
  public void initData() {
    APIClient.getArticles()
        // 在调用OnNext()之前先调用这个方法,这里的OnNext()是由Retrofit去调用的。
        .doOnNext(articles -> {
          Log.e("MainActivity", " -> initData: itemsSize = " + articles.items.size());
          articles.items.remove(0);
        })
        // 用上一个Observable返回的结果来创建一个新的Observable
        .flatMap(articles -> Observable.from(articles.items)) // 这里使用了from方法创建Observable
        // 指定接收者的工作线程
        .observeOn(AndroidSchedulers.mainThread())
        // 发射给订阅者
        .subscribe(
            // 创建订阅者
            new Subscriber<Articles.ItemsTestBean>() {
              @Override
              public void onCompleted() {

            }

              @Override
              public void onError(Throwable e) {

            }

              @Override
              public void onNext(Articles.ItemsTestBean itemsTestBean) {
                Log.e("MainActivity", " -> onNext: item " + i++ + " = " + itemsTestBean.id);
              }
            });
  }

  //需要测试的第二个方法,这是一个私有的void返回值方法,并且它修改的是私有参数
  private void initViewData() {
    viewWrapper = new ViewWrapper(ll);
  }

  private class ViewWrapper {
    View view;

    public ViewWrapper(View view) {
      this.view = view;
    }

    public int getWidth() {
      return view.getMeasuredWidth();
    }

    public void setWidth(int width) {
      view.getLayoutParams().width = width;
      view.requestLayout();
    }
  }
}


"测试类:"
//这里使用的是继承TestCase
public class MainActivityTest extends TestCase {
  MainActivity mainActivity;

  //该方法会在每次测试前执行,主要是为了初始化测试所需的对象、数据、配置之类的
  @Before
  public void setUp() throws Exception {
    mainActivity = new MainActivity();
  }

  //该方法发在每次测试结束后都会调用一次
  @Override
  protected void tearDown() throws Exception {
    super.tearDown();
    System.out.println("________");
  }

  //测试耗时操作。因为MainActivity中的这个方法主要执行了这个网络请求,但是我需要
  //获得它的回调,在回调中把阻塞的测试线程解除阻塞,并且这里只是味了检查获取结果是否
  //正确,所以我就直接测试了这个耗时操作,而不是MainActivity中的方法。
  @Test //使用Junit测试必须加的注解符
  public void testInitData() throws Throwable {
    //创建阻塞变量,参数为多少,就需要调用多少次countDown(),每次调用,阻塞变量-1,
    //这里阻塞变量值为1,所以就调用一次。
    final CountDownLatch latch = new CountDownLatch(1);
    APIClient.getArticles().subscribe(new Action1<Articles>() {
      @Override
      public void call(Articles articles) {
        System.out.println(articles.toString()); //在回调中验证网络请求的结果是否正确
        "可以看到,我们不需要再把软件运行起来,就可以检查所写的接口是否正确了。是不是很强大呢!!"
        latch.countDown();
      }
    });
    //在确保阻塞变量为0时,需要调用这个方法来终止阻塞。
    latch.await();
    System.out.println("_________________test");
  }

  //下面测试的方法没有返回值,而且是私有方法。因此我决定通过反射来调用这个方法,然后
  //判断在方法中创建的对象ViewWrapper是否创建成功,即不为null。
  @Test
  public void testInitViewData() throws Exception {
    ReflectUtils.invokeMethod(MainActivity.class, "initViewData"); //这是我封装的通过反射调用私有方法的方法,现在只需要知道它能调用到类中的私有方法就行了。
    assertEquals(null, ReflectUtils.getVariable(MainActivity.class, "viewWrapper")); //同样通过反射来获得私有属性。
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值