2

Currently we are developing the data access layer of a CRUD application. We have the following structure:

  • We have business entities which are used outside the data access layer to perform the business logic.
  • We have DAO entities which are used in the data access layer to store the data in our database. These entities are only data objects that map one to one to our database tables. They live inside our data access layer and cannot be used outside of this layer.
  • We have a class, let’s call it DatabaseAPI, responsible which offers the crud API for our business entities. For example it has the method EntityX createEntityX(EntityX entityToCreate) where EntityX is a business entity. This method is responsible for transforming the entity received into the corresponding DAO entities (some business entities map to more than one DAO entity), save the DAO entities and transform and return the resulting DAO entities back into the business entity.

We are currently having a discussion on how to properly test the DatabaseAPI methods. Particularly we have the following restriction: EntityX has a name field, there cannot exist two EntityXs with the same name. This restriction is enforced by the DatabaseAPI (currently as a unique constraint in our database, but we could use a query inside a transaction to check it manually), therefore we are adding a test to check that such restriction is correctly enforced, however we have the following disagreement:

  • I think the test should create an EntityX with a given name Y using the method createEntityX. Then try to create another EntityX using the same name Y, again with method createEntityX, and check that it fails with the correct exception.
  • My coworker thinks we should create an EntityXDAO, the correspondig DAO entity (or entities if there is more than one), with name Y in the test (manually, as DatabaseAPI methods don’t accept DAO entities in it's API by design). Then try to create another EntityX using the same name Y, this time using method createEntityX, and check that it fails with the correct exception.

So basically, we disagree on the test setup. My reasoning is:

  • The DAO entities are an implementation detail and should be avoided on the tests.
  • The restriction we are testing is about EntityX, not about EntityXDAO. Therefore, the test should be on terms of EntityX.
  • We already have a previous test which checks that createEntityX correctly creates an EntityX when there are no conflicts.

My coworker’s reasoning is:

  • In my proposed test, we would be using the method createEntityX do the test setup and then using it again for the actual test, checking that createEntityX throws the correct exception if an entity with the given name already exists.

The question is, should our test setup use the createEntityX method in its setup? Should we manually create the required DAO entities? Are there any alternative we are missing?

Example code:

void myTest()
{
    EntityX entityToCreateInDatabase = instantiateBusinessEntityX()
    databaseAPI.createEntityX(entityToCreateInDatabase)
    Assertions.assertThrows(MyDuplicityError.class, () -> databaseAPI.createEntityX(entityToCreateInDatabase))
}

void coworkersTest()
{
    EntityX entityToCreateInDatabase = instantiateBusinessEntityX()
    databaseAPI.getEntityDAO<EntityXDAO, Long>().create(new EntityXDAO(entityToCreateInDatabase))
    Assertions.assertThrows(MyDuplicityError.class, () -> databaseAPI.createEntityX(entityToCreateInDatabase))
}
  • 2
    There's something not quite right with your prose. The phrase "we would be using createEntityX to test createEntityX" doesn't make sense to me. – Robert Harvey Oct 12 '17 at 14:40
  • 2
    In any case, this would be a better question if we saw both test examples in code. Overall it's a good description, and would be even better if it accompanied the code. – Robert Harvey Oct 12 '17 at 14:42
  • 1
    When I saw "The DAO entities are an implementation detail and should be avoided on the tests," my first reaction was "not if it's an integration test," and my second reaction was "Do you intend to test the DAO entities?" – Robert Harvey Oct 12 '17 at 14:43
  • 2
    Finally, mocking out the code that deals with the database in order to isolate a layer for testing is a perfectly valid technique. – Robert Harvey Oct 12 '17 at 14:54
  • 1
    @ThomasJunk it's different since the code is doing the check here, so it's all internal and does not depend on the DB. The DB could actually be a local sqlite file, or even JSON for that matter. That check should still be there and fully functional. – Steve Chamaillard Oct 12 '17 at 15:14
1

You seem to be confused about what you're testing which could explain why you disagree on how to test.

You state :

therefore we are adding a test to check that such restriction is correctly enforced

You're testing that when the external data source used by DatabaseAPI says EntityX with name Y already exists, DatabaseAPI should throw an exception. This external data source says EntityX with name Y already exists by actually returning a DAO corresponding to the EntityX you're looking for (at least up to its name).

So, you could simply do this :

void myTest()
{
    Mock dataSourceMock = mock(DataSourceInterface)
    when(dataSourceMock)->receive('findEntityX')->with('name', 'Y')->return(new EntityXDAO(entityToCreateInDatabase))
    DatabaseAPI databaseAPI = instantiateDatabaseAPI(dataSourceMock)
    Assertions.assertThrows(MyDuplicityError.class, () -> databaseAPI.createEntityX(entityToCreateInDatabase))
}

Where DataSourceInterface should be implemented by your actual database layer which does queries.

  • I really like this approach, but I feel it has a minor problem. Currently we are checking this restriction using a unique constraint as a said in the question, it looks like this test doesn’t work if the validation is not done in code. We of course could mock another method of the DataSourceInterface to make it work in that case, probably createEntityX. But we would like the test to keep working regardless of if we are checking it in code or using a database constraint or another mechanism. – jesm00 Oct 12 '17 at 16:22
  • So that's another test. An integration test that's using your DataSourceInterface implementation and which needs the database to work. Testing something else means another test. – Steve Chamaillard Oct 12 '17 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.