Question

As an example, I have a simple extractor, Planex, that takes apart .plan file strings and puts them back together. I have a few unit tests on it that pretty thoroughly define its behavior. Here's the extractor:

object Planex {
  def apply(metadata: String, plan: String) = {
    String.format("%1$sPlan:\n%2$s", metadata, plan)
  }
  def unapply(str: String) = {
    val ixPlanLabel = str indexOf "Plan:"
    when(ixPlanLabel>=0) {
      (str take ixPlanLabel, (str drop (ixPlanLabel+5)).trim)
    }
  }
}

I have an Actor that uses this.

class PlanRepo(env: {
  val jsonFormats: Formats
  val load: () => String
  val planTarget: String => Unit
}) extends Actor {
  implicit val jsonFormats = env.jsonFormats
  def receive = {
    case (metaData: String, plan: Plan) => {
      val s = Planex(metaData,write(plan))
      env.planTarget(s)
    }
    case r: PlanRequest => {
      val Planex(metaData, planStr) = env.load()
      self.channel ! (metaData, read[Plan](planStr))
    }
  }
}

From my tests on PlanRepo, I'm able to pass in all of its dependencies, except for Planex. For that, I'm still using the concrete extractor object. So my repo test is actually also testing the separately-tested behavior of Planex. Is there a way around this?

I have similar problems with a set of nested case classes defining the hierarchy of the json part of this document - I'm having trouble separating them from each other.

Was it helpful?

Solution

Define the extractors in a trait, and then just have Planex extend that trait instead of implementing them directly.

trait PlanexExtractor {
  def apply(metadata: String, plan: String) = {
    String.format("%1$sPlan:\n%2$s", metadata, plan)
  }
  def unapply(str: String) = {
    val ixPlanLabel = str indexOf "Plan:"
    when(ixPlanLabel>=0) {
      (str take ixPlanLabel, (str drop (ixPlanLabel+5)).trim)
    }
  }
}

object Planex extends PlanexExtractor

Or, if you prefer, define the interface in the trait, and implement it in the object that extends that trait.

trait PlanexAPI {
  def apply(metadata: String, plan: String): String
  def unapply(str: String): Option[String]
  }
}

object Planex extends PlanexAPI {
  def apply(metadata: String, plan: String) = {
    String.format("%1$sPlan:\n%2$s", metadata, plan)
  }
  def unapply(str: String) = {
    val ixPlanLabel = str indexOf "Plan:"
    when(ixPlanLabel>=0) {
      (str take ixPlanLabel, (str drop (ixPlanLabel+5)).trim)
    }
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top