【问题标题】:How to test MVVM with DataBinding on Android如何在 Android 上使用 DataBinding 测试 MVVM
【发布时间】:2016-05-16 04:50:04
【问题描述】:

我一直在网上搜索,但无法了解如何使用 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);
    }
}

然后我有一个使用它的 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);
    }
}

现在我很困惑如何让测试变得更好。我读过它,但我需要一个真实世界的例子。此代码来自blog here(如果重要)。

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

【问题讨论】:

    标签: android wpf mvvm data-binding


    【解决方案1】:

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

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

    您的 ViewModel 是紧密耦合的,因为您在 ViewModel 中实例化 MyCountDownTimer。因此,您不能再进行单元测试,因为每次测试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);
        }
    }
    

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

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

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

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

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

    【讨论】:

    • 您好,感谢您的反馈。您的测试是 CountDownTimerInterface。说得通。但在我的情况下,模型是什么?
    • 不,我没有测试CountDownTimerInterface。它被嘲笑。我正在测试 ViewModel 的行为(因为在此示例中没有要测试的数据)。在您的示例中,您使用MyCountDownTimer,并且不可能单独测试您的ViewModel,因为无论何时您调用this.mCountDownTimer.something,您都将测试计数器,而不是您的视图模型。我删除了这种耦合,通过用一个接口替换具体的计数器实现,这个接口将在测试期间被模拟(不使用真正的 MyCountDownTimer 类)
    • @j2emanue:这里是行为驱动设计 (BDD) en.wikipedia.org/wiki/Behavior-driven_development 的链接
    猜你喜欢
    • 2018-06-23
    • 1970-01-01
    • 2017-01-10
    • 2017-10-02
    • 1970-01-01
    • 2019-11-27
    • 2021-08-26
    • 2021-06-22
    • 1970-01-01
    相关资源
    最近更新 更多