Traxex1909
Traxex1909

Reputation: 2710

Mock injection using Mockito - Android

I've just started unit testing on Android. I've tried testing a simple calculator app and I'm stuck on a problem regarding injecting mocks.

As per my understanding there are two ways of injecting mocks. Using dependency injections or using annotations like @Mock and @InjectMocks.

So here's my problem:

My calculator class uses another class Vars that holds the default value of the variables I intend to use. This is the class I want to mock so that I can use other variables instead of the default ones.

@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();

}

However when I try using mockVar for any of the operations, the default value stored inside Vars is used instead that of mockVar which probably means that the mocked variable is not injected properly. Can anyone point out where I've went wrong ?

EDIT : Updating question with code from 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;
}
}

Upvotes: 0

Views: 3217

Answers (2)

Andrei
Andrei

Reputation: 51

This is the classical problem where people confuse the notion of partial mocks introduced by Mockito.

You need to inject this mocked Var dependency in your Activity and this is tough since Android is managing the entire life cycle of activities. So in order to achieve your desired outcome you either use Dagger framework from Square or you use Service Locator Pattern if you don't want to introduce third party dependencies in your app.

Here is a more detailed article into Service Locator Pattern for injecting dependencies in your Activity: Improving Android Testing with Service Locator Pattern

Upvotes: 0

Saurabh
Saurabh

Reputation: 2472

  1. Since you have already used @Mock for Vars class, you don't need to use -

    mockVar = mock(Vars.class);

  2. You need to stub the calls on you mock object. Instead of -

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

You need to stub your getters (I am sure there will be methods like getn1() in Vars class) -

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

And then write your test.

Upvotes: 3

Related Questions