如何在 Android 上使用 DataBinding 测试 MVVM

j2e*_*nue 6 data-binding wpf android mvvm

我一直在网上搜索,但我无法找到如何使用 MVVM 更好地进行测试。我有一个与视图接口的 viewModel 的想法,但我不知道如何使用 MVVM 编写好的测试用例。我已经在 Android 中有以下 ViewModel:

public class ViewModel extends BaseObservable {
    private long countDownTime;
    private MyCountDownTimer mCountDownTimer;
    private final String TAG = getClass().getSimpleName();

    @Bindable    
    public long getCountDownTime() {
        return countDownTime;
    }


    public void setCountDownTime(long countDownTime) {
        this.countDownTime = countDownTime;

        notifyPropertyChanged((int) BR.countDownTime);
        Log.d(TAG,"prime tick:"+countDownTime);
    }

    public void startCounting(Long milli){
        mCountDownTimer.restartTimer(milli);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我有一个使用它的 xml 视图。我还有一个活动,它实际上将 xml 绑定到这个视图。此活动如下所示:

public class MainActivity extends FragmentActivity {
    CountdownBinder mCountdownBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //setContentView(R.layout.activity_main);
        mCountdownBinder = DataBindingUtil.setContentView(this, R.layout.activity_main);

        //Lets reference our textview just for fun
        mCountdownBinder.tvGreen.setText("initial text");
        ViewModel viewModel = ViewModel.instance();

        //now tell databinding about your viewModel below
        mCountdownBinder.setViewModel(viewModel);
        viewModel.startCounting(200000L);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我很困惑如何使测试更好。我读过它,但我需要一个真实世界的例子。如果重要的话,此代码来自此处博客

显然我可以更轻松地测试我的单元测试,对吗?我只会在 MVVM 中测试 viewModel 吗?主要需要测试什么?

Tse*_*eng 5

您的假设是正确的,即您只对 ViewModel 和模型进行单元测试。UI 本身不通过单元测试进行测试,但您也可以进行自动化 UI 测试,这与单元测试不同。

到目前为止,您的示例类本身并不是非常适合单元测试。MVVM 试图完成的主要事情之一(除了关注点分离)是解耦您的代码。您的 ViewModel 应该只包含表示逻辑,而不能包含业务逻辑。这进入了 MVVM 的模型层。

您的 ViewModel 是紧密耦合的,因为您正在MyCountDownTimerViewModel中实例化。因此,您不能再进行单元测试,因为每次测试ViewModel课程时,您还将测试MyCountDownTimer. 这会将您的单元测试变成集成测试(测试多个组件一起工作)。

每个定义的单元测试应该只测试一个非常特定的类型/类或某个代码块。换句话说,一个代码单元,因此得名:单元测试。为此,您需要解耦要测试的对象的依赖关系。

您通过将对象拆分为接口和实现来解耦对象,然后通常通过构造函数注入将具体实现注入到您的对象中。

例如:

public class ViewModel extends BaseObservable {
    private long countDownTime;
    // Use final keyword here, so mCountDownTimer can only be set in the constructor and never changed
    // this enforces the the classes invariants and once initialized, you'll be sure
    // that it never can be null, so no need to do null checks before using
    private final MyCountDownTimerInterface mCountDownTimer;
    private final String TAG = getClass().getSimpleName();

    public ViewModel(MyCountDownTimerInterface mCountDownTimer) {
        if(countDownTimer == null) {
            throw new IllegalArgumentException("countDownTimer can't be null. ");
        }

        this.mCountDownTimer = countDownTimer;
    }

    @Bindable
    public long getCountDownTime() {
        return countDownTime;
    }


    public void setCountDownTime(long countDownTime) {
        this.countDownTime = countDownTime;

        notifyPropertyChanged((int) BR.countDownTime);
    }

    public void startCounting(Long milli) {
        this.mCountDownTimer.restartTimer(milli);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以在没有类的具体实例的情况下测试您的 ViewModel MyCountDownTimer

由于您的示例仅包含行为而没有结果测试,因此您必须在您的示例中进行行为测试,例如

  • 如果我调用startCounting(10L),则restartTimer(10L)必须被调用MyCountDownTimerInterface 并且 getCountDownTime()必须返回10L

为此,您必须模拟MyCountDownTimerInterface接口并将模拟对象传递到内部。可以设置模拟来验证模拟接口的某个方法是否使用某个参数调用。

我无法为此提供任何代码,因为我不熟悉 Java/Android Mock 框架。我是 C#/.NET 开发人员。但是,如果您不知道如何为行为驱动的单元测试模拟接口,请在 StackOverflow 上提出一个新问题:)