Wednesday, December 18, 2013

How to make ormlite and robolectric work together.
I use Roboguice also, this is my RoboInjectedTestRunner:

public class RoboInjectedTestRunner extends RobolectricTestRunner {

 public RoboInjectedTestRunner(Class testClass)
   throws InitializationError {
  super(testClass);
 }

 @Override
 protected Class getTestLifecycleClass() {
  return TestLifeCycleWithInjection.class;
 }

 public static class TestLifeCycleWithInjection extends DefaultTestLifecycle {

  @Override
  public void prepareTest(Object test) {
   Application application = Robolectric.application;

   AbstractModule eftelingModule = new EftelingModule();
   AbstractModule testModule = new TestModule();

   RoboGuice.setBaseApplicationInjector(application,
     RoboGuice.DEFAULT_STAGE,
     RoboGuice.newDefaultRoboModule(application), eftelingModule, testModule);

   RoboGuice.getInjector(application).injectMembers(test);
  }

 }
}


If you don't use Roboguice, you can use the default RobolectricTestRunner instead.
This is the annotations I have for my test classes:
@RunWith(RobolectricTestRunner.class)
@Config( shadows = {ShadowCaseSensitiveSQLiteCursor.class})
@UsingDatabaseMap(SQLiteMap.class)
public class APITest {
...
}

This is the ShadowCaseSensitiveSQLiteCursor: (it's from here:http://stackoverflow.com/questions/11833150/robolectric-testing-with-ormlite so please give that post a credit)
/**
 * Simulates an Android Cursor object, by wrapping a JDBC ResultSet.
 */
@Implements(value = SQLiteCursor.class, inheritImplementationMethods = true)
public class ShadowCaseSensitiveSQLiteCursor extends ShadowSQLiteCursor {
  private ResultSet resultSet;

  public void __constructor__(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
  }

  /**
   * Stores the column names so they are retrievable after the resultSet has closed
   */
  private void cacheColumnNames(ResultSet rs) {
    try {
      ResultSetMetaData metaData = rs.getMetaData();
      int columnCount = metaData.getColumnCount();
      columnNameArray = new String[columnCount];
      for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
        String cName = metaData.getColumnName(columnIndex);
        this.columnNames.put(cName, columnIndex - 1);
        this.columnNameArray[columnIndex - 1] = cName;
      }
    } catch (SQLException e) {
      throw new RuntimeException("SQL exception in cacheColumnNames", e);
    }
  }

  private Integer getColIndex(String columnName) {
    if (columnName == null) {
      return -1;
    }

    Integer i = this.columnNames.get(columnName.toLowerCase());
    if (i == null) return -1;
    return i;
  }

  @Implementation
  public int getColumnIndex(String columnName) {
    return getColIndex(columnName);
  }

  @Implementation
  public int getColumnIndexOrThrow(String columnName) {
    Integer columnIndex = getColIndex(columnName);
    if (columnIndex == -1) {
      throw new IllegalArgumentException("Column index does not exist");
    }
    return columnIndex;
  }

  public void checkPosition() {
    if (-1 == currentRowNumber || getCount() == currentRowNumber) {
      throw new IndexOutOfBoundsException(currentRowNumber + " " + getCount());
    }
  }

  @Implementation
  public void close() {
    if (resultSet == null) {
      return;
    }

    try {
      resultSet.close();
      resultSet = null;
      rows = null;
      currentRow = null;
    } catch (SQLException e) {
      throw new RuntimeException("SQL exception in close", e);
    }
  }

  @Implementation
  public boolean isClosed() {
    return (resultSet == null);
  }

  private Map fillRowValues(ResultSet rs) throws SQLException {
    Map row = new HashMap();
    for (String s : getColumnNames()) {
      row.put(s, rs.getObject(s));
    }
    return row;
  }

  private void fillRows(String sql, Connection connection) throws SQLException {
    //ResultSets in SQLite\Android are only TYPE_FORWARD_ONLY. Android caches results in the WindowedCursor to allow moveToPrevious() to function.
    //Robolectric will have to cache the results too. In the rows map.
    Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
    ResultSet rs = statement.executeQuery(sql);
    int count = 0;
    if (rs.next()) {
      do {
        Map row = fillRowValues(rs);
        rows.put(count, row);
        count++;
      } while (rs.next());
    } else {
      rs.close();
    }

    rowCount = count;

  }

  public void setResultSet(ResultSet result, String sql) {
    this.resultSet = result;
    rowCount = 0;

    //Cache all rows.  Caching rows should be thought of as a simple replacement for ShadowCursorWindow
    if (resultSet != null) {
      cacheColumnNames(resultSet);
      try {
        fillRows(sql, result.getStatement().getConnection());
      } catch (SQLException e) {
        throw new RuntimeException("SQL exception in setResultSet", e);
      }
    }
  }
}

No comments: