简体   繁体   中英

Replace field values of a nested object dynamically

I am trying to write integration test for my scala application(with akka-http ). I am running into a problem, for which I am not able to find a solution.

My Case classes are as below:

case class Employee(id:Long, name:String, departmentId:Long, createdDate:Timestamp) extends BaseEntity
case class EmployeeContainer(employee:Employee, department:Department)  extends BaseEntity

I have a method like this

trait BaseTrait[E<:BaseEntity, C <: BaseEntity]{
    def getById(id:Long): Future[List[C]] = {
       //query from db and return result.
    }

    def save(obj:E) = {
      //set the createDate field to the current timestamp
      //insert into database
    }

}

I can extend my class with BaseTrait and just override the getById() method. Rest of the layers are provided by our internal framework.

class MyDao extends BaseTrait[Employee, EmployeeContainer] {
  override def getById(id:Long) = {
      for {
      val emp <- getFromDb(id)
      val dept <- DeptDao.getFromDb(emp.departmentId)
      val container = EmployeeContainer(emp,dept)
      } yield(container)
   }
}

So in the rest layer, I will be getting the response as the EmployeeContainer . The problem now I am facing is that, the modified date is automaticaally updated with the current timestamp. So, when I get back the result, the timestamp in the object I passed to save() method will be overwritten with the current time. When I write the test case, I need to have an object to compare to. But the timestamp of that object and the one I get abck will never be the same.

Is there anyway, in which I can replace all the occurrance of createDate with a known value of timestamp so that I can compare it in my testcase? The main problem is that I can not predict the structure of the container (it can have multiple case classes(nested or flat) with or without createDate fields).

I was able to replace the field using reflection if it comes in the main case class, but unable to do for nested structures.

You probably need to use some for of Inversion of Control . Your main problem is that you are calling the db directly: val emp <- getFromDb(id) and thus have no control on a test of the values that are received. Calling the DB on a unit test is also arguably a bad idea, since it expands the unit to the entire database layer. You want to test a small, self-contained unit.

A simple solution is to encapsulate your DB calls as an interface and pass an instance of that interface. For instance:

    class MyDao extends BaseTrait[Employee, EmployeeContainer](val dbCall: Long => Employee) {
  override def getById(id:Long) = {
      for {
      val emp <- dbCall(id)
      val dept <- DeptDao.getFromDb(emp.departmentId)
      val container = EmployeeContainer(emp,dept)
      } yield(container)
   }
   }

Then you can simply use new MyDao(getFromDb) for normal code and val testDao = new MyDao(id => Employee(id, myFixedName, myFixedDeptID, myFixedTestDate)) from test code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM