Java unties database manipulation code and makes it available for testing

I want to write test for the following method:

public void addItem(Item item) {

    items.add(0, item);
    DatabaseHelper.getInstance().writeOneItem(item);
}

The class is called ItemManager, and it must manage items that the user can save or remove from the list. It should be kept in sync with the Sqlite database, which stores items in a list.

When DatabaseHelper(ormlite) is not included in the number init(Context context)(which usually starts when the Andoid application starts, but does not execute in my test), the method getInstance()returns null, and the method above will crash.

What am I supposed to do here? I could just call init(Context context)from my test or check that DatabaseManager.getInstance()it is null before calling anything on it. But this is more like a workaround. It seems to me that I should not do any database materials in this method and try to separate the ItemManager from the database as much as possible.

Any ideas on how the perfect solution would look, not in the form of a specific implementation, but from a design point of view?

I am new to unit testing and have difficulty decoupling things with each other.

+4
source share
4 answers

, ItemManager DatabaseHelper , unit test , . , DatabaseHelper , .

: DatabaseHelper.getInstance() . ItemManager DatabaseHelper. , .

Mockito, :

public void addItem(Item item) {
    items.add(0, item);
    this.databaseHelper.writeOneItem(item);
}

@Test
public void my_test() {
    // GIVEN
    DatabaseHelper databaseHelper = mock(DatbaseHelper.class);
    ItemManager manager = new ItemManager(databaseHelper);
    Item item = new Item()

    // WHEN
    manager.addItem(item);

    // THEN
    verify(databaseHelper).writeOneItem(item); // This verifies that the method writeOneItem of the "mock" is called with the "item" parameter
}

// Another test would check that the item is added to the "items" collection

unit test ONE, , .

DatabaseHelper ItemManager , : , , ..

+5

(, mockito)

class ItemManager {
 ...
 // decoupling
 private DatabaseHelper instance = DatabaseHelper.getInstance();

 public void addItem(Item item) {
    items.add(0, item);
    instance.writeOneItem(item);
 }
}

mockito:

class ItemManagerTest{
 // declare mock service
 @Mock
 DatabaseHelper instance;

 // inject mock service into your about to be tested class
 @InjectMocks
 ItemManager manager;

 @Test
 public void test() {
  // Given
  Item item = new Item();
  ...

  // When
  manager.addItem(item);

  // Then
  // assert that the service has been called with the right parameters
  verify(instance).writeOneItem(item);
 }
+3

, DatabaseHelper, , MyDBLayer

class abstract MyDBLayer {
    public void writeOneItem(Item item);
}

class OrmLiteDBLayer {
    public void writeOneItem(Item item) {
        DatabaseHelper.getInstance().writeOneItem(item);
    }
}

class FakeDBLayer {
    public void writeOneItem(Item item) {
        // do nothing
    }
}

FakeDBLayer OrmLiteDBLayer .

+1

, , items , .

.

- DatabseHelper.getInstance() , .

public class ClassToTest {

  private DatabaseHelper databaseHelper;

  public void setDatabaseHelper(DatabaseHelper databaseHelper) {
    this.databaseHelper = databaseHelper;
  }

  public void addItem(Item item) {
    items.add(0, item);
    databaseHelper.writeOneItem(item);
  }
}

IDatabaseHelper void writeOneItem(Item item) DatabaseHelper . , MockDatabaseHelper, .

ClassToTest myClass = new ClassToTest();
myClass.setDatabaseHelper(DatabaseHelper.getInstance());

ClassToTest myClass = new ClassToTest();
myClass.setDatabaseHelper(new MockDatabaseHelper());

An implementation in MockDatabaseHelpermay be empty or a simple log operation. If your class uses other methods from DatabaseHelper, you also need to add this interface and add an implementation to the MockDatabaseHelper, which mimics the behavior of a real DatabaseHelper.

As mentioned earlier, there are mock frameworks that save you the trouble of writing MockObjects. In addition, I recommend checking out Injection Dependency in general.

+1
source

All Articles