Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking SQLite-Database while testing Activity with Robolectric

In the past few days I started playing around with roboguice, robolectric and mockito. I have a small Android-application with a login-screen containing an AutoCompleteTextView for faster entering the username. The usernames for the AutoCompleteTextView are stored in a sqlite-database.

public class MainActivity extends RoboActivity implements View.OnClickListener {
@InjectView(R.id.startScreen_Login_Button) private Button loginButton;
@InjectView(R.id.startScreen_Cancel_Button) private Button cancelButton;
@InjectView(R.id.startScreen_forgotPwd_TextView) private TextView forgotPWTextView;
@InjectView(R.id.startScreen_Username_AutoCompleteTextView) private AutoCompleteTextView loginUsernameAutoCompleteTextView;
@InjectView(R.id.startScreen_Password_EditText) private EditText loginPasswordEditText;
@Inject private SharedPreferences sharedPreferences;
@Inject SQLiteDBAdapter dbAdapter;

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

    loginButton.setOnClickListener(this);
    cancelButton.setOnClickListener(this);
    forgotPWTextView.setOnClickListener(this);

    // Creating List for startScreen_Username_AutoCompleteTextView
    List<User> userList = dbAdapter.getUserList();
    ListIterator<User> it = userList.listIterator();
    List<String> userStringList = new ArrayList<String>();
    User user;
    while (it.hasNext()) {
        user = it.next();
        userStringList.add(user.getName());
    }

    loginUsernameAutoCompleteTextView.setAdapter(new ArrayAdapter<String>(this, R.layout.select_page_row, userStringList));
    }
...
}

I want to test MainActivity using robolectric, trying to mock the database with mockito. This is my test-class:

@RunWith(CustomRobolectricTestRunner.class)
public class MainActivityTest {

@Mock
SQLiteDBAdapter dbAdapter;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

@Test
public void shouldHaveApplicationName() throws Exception {
    String appName = new MainActivity().getResources().getString(R.string.app_name);
    assertThat(appName, equalTo("OperationReport"));
}

@Test
public void testButtonsVisible()
{
    MainActivity mainActivity = new MainActivity();
    mainActivity.onCreate(null);
}
}

Calling mainActivity.onCreate(null); is starting the error-cascade, ending in line Cursor cursor = db.rawQuery(SQL_QUERY, null); of my getUserList-method in my SQLiteDBAdapter:

public List<User> getUserList() {
    SQLiteDatabase db = getReadableDatabase();
    List<User> userList = new ArrayList<User>();

    String SQL_QUERY = "SELECT * FROM User;";
    Cursor cursor = db.rawQuery(SQL_QUERY, null);
    cursor.moveToFirst();
    while (!cursor.isAfterLast()) {
        User user = new User();
        user.setUserUUID(cursor.getString(0));
        user.setName(cursor.getString(1));
        user.setPassword(cursor.getString(2));
        user.setDateOfBirth(cursor.getString(3));
        user.setStaffNumber(cursor.getString(4));
        user.setActive(cursor.getInt(5));
        user.setUserClass(cursor.getInt(6));
        userList.add(user);
        cursor.moveToNext();
    }
    cursor.close();
    db.close();
    return userList;
}

I read, that a Mock is returning empty stubs of void-methods, and returns null on any other method. As I am mocking the SQLiteDBAdapter-class I am expecting that calling getUserList on my mocked SQLiteDBAdapter returns null. It is not quite clear to me, why he is accessing the original method. I guess it is still using the original SQLiteDBAdapter and not the Mock. What do I have to do to fix this, and how is it working? I ran out of ideas, so any help is appreciated.

like image 904
Frank Avatar asked Apr 10 '13 22:04

Frank


1 Answers

Mocking a database to test a DAO makes no sense to me at all. What are you testing? The database. Why eliminate it?

Mocking the database makes sense once you have all your DAO tests passing and it's time to test the service that users it to fulfill a unit of work. You've already tested the DAO and the database, and your service unit test need not be an integration test. By all means mock away in that case.

I don't know a lot about what you're mocking, but when I mock it's for interfaces of my making. The mock provides a stand-in implementation for the interface-typed reference that my client/test is using.

If you're trying to mock a concrete class I'd recommend wrapping that adapter inside an interface-based implementation. It'll be a better abstraction and you'll have an easier time mocking your interface.

like image 165
duffymo Avatar answered Oct 08 '22 19:10

duffymo