【问题标题】:Mock injection using Mockito - Android使用 Mockito 模拟注入 - Android
【发布时间】:2013-08-13 05:54:52
【问题描述】:

我刚刚开始在 Android 上进行单元测试。我试过测试一个简单的计算器应用程序,但遇到了一个关于注入模拟的问题。

据我了解,有两种注入模拟的方法。使用依赖注入或使用 @Mock@InjectMocks 等注解。

这是我的问题:

我的计算器类使用另一个类Vars,它保存我打算使用的变量的默认值。这是我要模拟的类,以便我可以使用其他变量而不是默认变量。

@RunWith(MockitoJUnitRunner.class)
public class CalcActivityTest extends ActivityUnitTestCase<CalcActivity> {

private Intent in;
private Button btnAdd,btnSub,btnMul,btnDiv,btnDef;
private TextView res;
@InjectMocks
private CalcActivity mActivity;
@Mock
private Vars mockVar;


public CalcActivityTest() {
    super(CalcActivity.class);
}

@Before
protected void setUp() throws Exception{

    super.setUp();
    in = new Intent(getInstrumentation().getTargetContext(),CalcActivity.class);
    in.putExtra("num1", 20.0);
    in.putExtra("num2",20.0);
    startActivity(in, null, null);
    mockVar = mock(Vars.class);
    mockVar.setn1(20);
    mockVar.setn2(40);

    mActivity = getActivity();

}

但是,当我尝试对任何操作使用 mockVar 时,会使用存储在 Vars 中的默认值而不是 mockVar,这可能意味着模拟变量没有正确注入。谁能指出我哪里出错了?

编辑:使用 CalcActivity 中的代码更新问题

package com.example.advancedcalc;

import com.example.advancedcalc.R;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class CalcActivity extends Activity implements OnClickListener {

private Button btnAdd,btnSub,btnMul,btnDiv,btnDefault;
private TextView res;

private double n1,n2;

private Vars var;

public CalcActivity(Vars var){
    this.var  = var;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_calc);

    btnAdd = (Button) findViewById(R.id.add);
    btnSub = (Button) findViewById(R.id.sub);
    btnMul = (Button) findViewById(R.id.mul);
    btnDiv = (Button) findViewById(R.id.div);

    btnDefault = (Button) findViewById(R.id.btnDef);

    res = (TextView) findViewById(R.id.res);

    Bundle extras = getIntent().getExtras();
    n1 = extras.getDouble("num1");
    n2 = extras.getDouble("num2");

    var = new Vars();

    btnAdd.setOnClickListener(this);
    btnSub.setOnClickListener(this);
    btnMul.setOnClickListener(this);
    btnDiv.setOnClickListener(this);

    btnDefault.setOnClickListener(this);

}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.calc, menu);
    return true;
}

@Override
public void onClick(View v) {
    switch(v.getId()){
    case R.id.add: 
        res.setText(String.valueOf(add(n1,n2)));
        break;
    case R.id.sub: 
        res.setText(String.valueOf(sub(n1,n2)));
        break;
    case R.id.mul: 
        res.setText(String.valueOf(mul(n1,n2)));
        break;
    case R.id.div: 
        res.setText(String.valueOf(div(n1,n2)));
        break;
    case R.id.btnDef:
        n1 = var.getn1();
        n2 = var.getn2();
    }

}

public double add(double n1,double n2){
    return n1+n2;
}
public double sub(double n1,double n2){
    return n1-n2;
}
public double mul(double n1,double n2){
    return n1*n2;
} 
public double div(double n1,double n2){
    return n1/n2;
}
}

【问题讨论】:

  • CalcActivity的代码是什么?为什么要在 setUp() 方法中为 mockVars 重新分配一个新的模拟,因为 Mock 注释已经创建了一个? getActivity() 是做什么的?
  • getActivity() 返回被测活动。我正在重新分配 mockVars 因为mockVar.set() 在没有它的情况下返回一个空指针异常。使用 CalcActivity 中的代码更新问题。
  • 被测活动应该是mActivity,因为这是 Mockito 用mockVar 注入的(在你在 setUp() 方法中重新初始化它之前创建的那个)。重新阅读这些注释的文档。您不应该对这些变量重新产生任何影响,否则注释将没有任何用途。
  • 我需要使用getActivity(),因为我需要调用findViewById() 方法来初始化我的CalcActivity 类中的不同UI 元素。不使用 getActivity() 会为我的所有 UI 元素返回一个空指针异常。
  • 那么你应该注入模拟 UI 元素,或者在 getActivity() 返回的真实活动上创建一个 Spy,而不是模拟。

标签: android mockito


【解决方案1】:
  1. 既然你已经用@Mock 做Vars 类了,你就不用再用了-

    mockVar = mock(Vars.class);

  2. 您需要对模拟对象的调用存根。而不是 -

    mockVar.setn1(20); mockVar.setn2(40);

你需要存根你的 getter(我相信 Vars 类中会有 getn1() 之类的方法)-

Mockito.when(mockVar.getn1()).thenReturn(20);
Mockito.when(mockVar.getn2()).thenReturn(40);

然后编写你的测试。

【讨论】:

  • 我按照您的建议进行了更改,使用 Mockito.when(mockVar.getn1()).thenReturn(20); 返回空指针异常。
  • 你在使用 MockitoAnnotations.initMocks(this);在 setup() 方法中。您应该在任何存根之前使用它。
  • 我现在已经添加了。模拟对象仍然没有被注入。我的班级仍在使用 Var 而不是 mockVar。
  • @InjectMocks 仅适用于使用 @Inject 注释的成员。您只创建了一个模拟变量,但没有注入到 CalcActivity 实例中。您应该在 CalcActivity 类中添加一个setVars(Vars var),并在测试用例的setUp() 方法中使用模拟变量调用它。
【解决方案2】:

这是一个经典问题,人们混淆了 Mockito 引入的部分模拟的概念。

您需要在 Activity 中注入这个模拟的 Var 依赖项,这很困难,因为 Android 正在管理 Activity 的整个生命周期。 因此,为了达到您想要的结果,您可以使用 Square 的 Dagger 框架,或者如果您不想在您的应用中引入第三方依赖项,您可以使用 Service Locator Pattern。

这里有一篇关于服务定位器模式的更详细的文章,用于在您的 Activity 中注入依赖项:Improving Android Testing with Service Locator Pattern

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-16
    • 1970-01-01
    • 2022-12-29
    • 1970-01-01
    • 2017-05-25
    • 2021-05-28
    相关资源
    最近更新 更多