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 methodEntityX createEntityX(EntityX entityToCreate)
whereEntityX
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 EntityX
s 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 nameY
using the methodcreateEntityX
. Then try to create anotherEntityX
using the same nameY
, again with methodcreateEntityX
, 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 nameY
in the test (manually, asDatabaseAPI
methods don’t accept DAO entities in it's API by design). Then try to create anotherEntityX
using the same nameY
, this time using methodcreateEntityX
, 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 aboutEntityXDAO
. Therefore, the test should be on terms ofEntityX
. - We already have a previous test which checks that
createEntityX
correctly creates anEntityX
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 thatcreateEntityX
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))
}
1 Answer 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, probablycreateEntityX
. 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– jesm002017年10月12日 16:22:53 +00:00Commented Oct 12, 2017 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– Steve Chamaillard2017年10月12日 18:08:33 +00:00Commented Oct 12, 2017 at 18:08
createEntityX
to testcreateEntityX
" doesn't make sense to me.