如何测试依赖于 CountDownTimer 的 Android 类

ido*_*w09 4 testing tdd dependencies android unit-testing

我正在为一个 android 应用程序编写一个 ViewModel,它应该实现并启动一个 CountDownTimer 以便不时更新一些 UI。

我最近开始实践 TDD,我想知道我应该做出哪些架构决策才能使 ViewModel 可测试(我希望测试快速运行而不依赖于实际的计时机制)。我无法提供 CountDownTimer 作为依赖项,因为它是在 ViewModel 本身中实现的抽象类,所以我“不知道”要提供什么实现。

一般来说,在使用具有严格约束和不可测试的框架代码的框架时,编写测试的最佳实践是什么?

这是我目前拥有的代码。你如何使它可测试?

import android.arch.lifecycle.ViewModel;
import android.os.CountDownTimer;

class MyViewModel extends ViewModel {

    private MyView myView;

    public void init(MyView myView) {
        this.myView = myView;
        new CountDownTimer(0, 1000) {
            @Override
            public void onTick(long l) {
                myView.updateUi(l);
            }

            @Override
            public void onFinish() {
                myView.updateUiFinished();
            }
        }.start();
    }

    public interface MyView {
        void updateUi(long l);

        void updateUiFinished();
    }
}
Run Code Online (Sandbox Code Playgroud)

Nko*_*osi 5

不要模拟/测试您不拥有的代码。将它们视为应该封装在您控制的抽象后面的第 3 方依赖项。在可测试性方面,这将具有更大的灵活性。

您当前的代码与CountDownTimer测试时更难以控制所需的行为紧密耦合。

import android.arch.lifecycle.ViewModel;
import android.os.CountDownTimer;

class MyViewModel extends ViewModel {
    private MyView myView;
    private MyCountDownTimer timer;

    public MyViewModel(MyCountDownTimer timer) {
        this.timer = timer;
    }

    public void init(MyView myView) {
        this.myView = myView;
        timer.attach(this.myView);
        timer.start();
    }

    public interface MyView {
        void updateUi(long l);
        void updateUiFinished();
    }

    public interface MyCountDownTimer { 
        void attach(MyView view);
        void start();
        void cancel();
    }
}

public class DefaultUiUpdateTimer extends MyViewModel.MyCountDownTimer {
    private CountDownTimer timer;

    public void attach(MyViewModel.MyView myView) {
        timer = new CountDownTimer(0, 1000) {
            @Override
            public void onTick(long l) {
                myView.updateUi(l);
            }

            @Override
            public void onFinish() {
                myView.updateUiFinished();
            }
        };
    }   

    public void start() {
        timer.start();
    }

    public void cancel() {
        timer.cancel();
    }
}
Run Code Online (Sandbox Code Playgroud)

MyViewModel现在与 解耦,CountDownTimer因为实例的创建已被反转,并且显式依赖项注入到构造函数中。您可以轻松地将它init与视图一起传递到方法中

public void init(MyView myView, MyCountDownTimer timer) {
    this.myView = myView;
    this.timer = timer;
    timer.attach(this.myView);
    timer.start();
}
Run Code Online (Sandbox Code Playgroud)

匿名CountDownTimer子类在技术上是一个实现问题,现在它已被提取并封装在它的关注点中,允许您执行您认为适合视图的任何操作。

为了测试视图模型,可以将模拟/存根/假货传递给测试的主题,并配置所需的行为以允许执行测试直至完成。