Mocking SQLite-Database while testing Activity with Robolectric
Asked Answered
D

1

7

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.

Diseased answered 10/4, 2013 at 22:16 Comment(0)
A
3

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.

Aggressive answered 10/4, 2013 at 22:18 Comment(4)
I want to test my Login-Activity. This Activity is using a database to get a userList for the AutoCompleteTextview. I want to replace the database with a Mock, so I can test the Login-Activity (Buttons, etc.) without being dependent on the database. I considered testing some Buttons and EditText a good exercise, since I am playing around with robolectric and mockito just for a few days.Diseased
OK, that's different - you're testing the UI. I'll assume that you've already tested the DAO, so my comments about the service hold for you.Aggressive
Mocking 3rd party code makes all the sense in the world to me. Why would you be testing someone else's code? And why do you want the overhead of ACTUALLY having a database. When you test the DAOs you aren't testing that the database or its driver functions as you expect, you're testing that your interaction with the database is as you expect. This is exactly what a mock is for. eg duplicate updates are not uncovered when hitting a real DB, but are typically not expected or wanted. This is uncovered by a mock. Unfortunately you can't control what is mockable, so abstracting is the only way.Weeper
Fair enough, but I you don't mean testing a 3rd party database. Service? Yes. You aren't accessing any 3rd party databases directly. The ones you'd test are YOUR databases. And I'm assuming that you'll test those - no mocks - and then mock them when you unit test services that use them.Aggressive

© 2022 - 2024 — McMap. All rights reserved.