Is there any nice way of creating "dumb" DAO objec...
# exposed
g
Is there any nice way of creating "dumb" DAO objects? Seems hard to mock to return values if the value returned is a DAO object. I'm getting
Property klass should be initialized before get.
which I guess is related that I do not have any relations given I just wanna return a dumb empty entity object for now.
a
Not entirely sure what you mean by a "dumb" DAO object, but my strategy for mocking databases has always instead been to use the pure-java in-memory H2 database as the underlying database for tests.
g
Ah, yeah; When I test my repository layer I have an in memory database. But I am thinking that the repository returns DAO objects, so then I want to mock them when I test my service layer.
But they seems not so dumb as a plain POJO class with hibernate for exampel.
So for example:
Copy code
when(myRepository).findSomething().thenReturn(MyDAOObject)
a
I'm not really sure how a resource returned by your repository would have anything worth mocking. I would typically have my repository return stateless data classes, and so any behavior to be mocked would be elsewhere.
Copy code
data class Cat(val id: Int, val name: String)

interface CatRepo {
  operator fun get(id: Int): Cat?
}

class ExposedCatRepo: CatRepo {
  .....
}
How do you have your access-patterns architected?
g
Yeah, I was thinking that the repository should return the actual DAO entities, and then the services would return models of said DAOs. But it seems problematic to creating the DAO objects, they have too much logic related to them. So I guess I would just return a model instead, given it is way easier to mock a simple data class.
But have a Table class, a DAO class and a model class which kinda all look the same seems like so much boiler plate code.
a
Personally, I'm not a fan of the exposed-dao module. The entity class is yet another layer between the database and the data class I want to ultimately work with. But even if you are working with DAO entities, I don't really understand how there's any behaviour to be mocked; considering you're working with an in-memory database for tests. The entity classes from the dao module should continue to work as intended in your tests, so long as they're within a transaction.
g
Yeah. I am just explaining it bad. I just wanna mock the repository in the service test. So I dont really wanna mock the entity itself. But I wanna mock the return value from a method call. And returning a DAO does not really work, given I can't create an "empty" dao with relations.
a
So you mean cut the database out entirely, like
Copy code
interface CatRepo {
  operator fun get(id: Int): Cat?
}

class ExposedCatRepo: CatRepo {
  ...
}

class FakeCatRepo: CatRepo {
  val cats = mutableListOf<Cat>()

  override fun get(id: Int) = cats.find { it.id == id}
}
g
It's even more simple then that. I just wanna be able to create a new entity of the DAO without too much trouble. The service contains some logic, and I only wanna test the logic in the service. My service ues the repository:
Copy code
@Test
fun `my awesometest`() {
    val cat = Cat(EntityId(1L, LongIdTable())
    when(myRepoMock).find(1L).thenReturn(cat)
}
a
Ok, so if you have an in-memory db (such as h2) injected as the underlying database in your tests, then there's no need to use any mocking framework. You just have to save the entity to the database like you normally would, and it will be available, like normal.
g
I use a h2 for my repository tests
I dont wanna use any database at all for my service tests
That's what I meant by the DAO classes not being just plain old POJOs. Like it could work with in hibernate for example.
a
Fair enough. So for your service tests, you want to completely mock the database layer. In that case, I don't think you can have your fake repository return exposed entities. You need to add a layer above the repository so that your mock returns the data class you want to work with. Although perhaps someone with more exposed experience can prove me wrong on that.
However, my completely biased recommendation would be to use h2 for every layer of tests, and limit the scope of your service to manage the complexity of spinning up the entire app for every test. I've written about it here; although it uses JDBC instead of exposed.
g
Hm... That's a interesting thought. Why would you recommend to use h2 for every layer? Would it not result in too much setting up for each test given we test multiple layers?
For example, say I had semi advanced repository method that fetches entities between ranges and a few other attributes. Then I would need to create the entities so they would match my query; just because I maybe want to make sure a few rows of logic in my service is applied.
(And that logic is tested in my repository test, but needs to be setup twice)
But yeah, anyways. I guess it is best to separate the service from the DAOs. But damn, I really dont like having a
Cats
table a
Cat
DAO and a
CatModel
with all the same properties almost. And not using DAO is sometimes problematic for fetching relations imo. It gives some value, but dunno if the trouble is worth it.
a
It's a tradeoff, and it works best in a microservice architecture, where setting up an single app instance can be done in milliseconds; the Spring framework will most likely disqualify you from using this method. If you set up your entire fake app for each test, then yes it requires more setup, but you can get away with far less tests to achieve the same test coverage. I often make myself a
TestDriver
class to perform common setup tasks and hold the entrypoints into the app. I agree the exposed-dao module makes it much easier to fetch relations, but then the tradeoff is an extra layer between the database and your data classes. As a personal rule, I would never have my repository return an exposed entity or ResultSet anyway; because your business logic calling the repository must then be polluted with the database transaction.
g
Yeah... The design will be rather strange if you do not return the DAO entities when using it underneath. Let's say we have a entity which could be created with relations. The repositories should not be dependent of each other. Then we only have one option, that is the repository method takes in the DAO entity as an parameter. But how would one get the entity from another repository if no repository returns the actual DAO.
So I guess: Do not use DAO.
165 Views