Как обеспечить альтернативную реализацию абстрактного интерфейса в Scala? - PullRequest
1 голос
/ 04 октября 2019

Вот проблема, с которой я сталкиваюсь:

У меня есть абстрактная черта Toggles, у которой есть абстрактный метод isToggleEnabled(name: String).

То, что я хочу иметь, - это иметь возможность иметь черту производственной среды ProdToggles с конкретной реализацией isToggleEnabled и использовать ее в коде продукта, но иметь возможность предоставлять переопределяющую альтернативную черту TestToggles с новойреализация isToggleEnabled при тестировании.

Причина заключается в том, что реализация производственной среды использует свойства System для хранения имен переключателей и их состояний, однако при тестировании я хотел бы предоставить другую реализацию для хранения и чтения переключателей, чтобы несколько тестов могли выполняться параллельно безвлияют друг на друга (как через свойства системы они будут). То, что я придумал, используя шаблон торта:

trait Toggleable {
  def isToggleEnabled(name: String): Boolean
}

trait ProdToggles extends Toggles with Toggleable {
  override def isToggleEnabled(name: String): Boolean = System.getProperty("name").toBoolean
}

trait TestToggles extends Toggles with Toggleable {
  val cache = scala.collection.mutable.HashMap.empty[String, Boolean]
  override def isToggleEnabled(name: String): Boolean = cache.getOrElse(name, false)
}

trait Toggles {
  this: Toggleable =>

  def isEnabled(name: String): Boolean = {
    isToggleEnabled(name)
  }
}

//—————IN PROD code—————

class Prod() {

  this: Toggles =>

  def doSomething(): Unit ={
    isEnabled("toggle.name")
  }
}

object Prod {
  def apply(): Prod = new Prod with ProdToggles
  def apply(testing: Boolean) = new Prod with TestToggles
}

//——————IN TEST code———————————
class Tests {
  val prod = Prod(true)
  prod.doSomething()
}

Однако проблема с этим:

  1. Нарушает инкапсуляцию и экземпляр Prod может быть неправильно использован как экземпляр Toggle как вымог бы сделать (new Prod with ProdToggles).{isEnabled, isToggleEnabled, doSomething}
  2. Каждому Prod классу mixin Toggles потребуется object с фабрикой apply для возврата пользовательского экземпляра для тестирования и для prod
  3. Cake-pattern isанти-паттерн

Есть ли у вас какие-либо другие подходы, чтобы обойти эту проблему? Спасибо!

1 Ответ

1 голос
/ 04 октября 2019

Я не уверен, что было целью всех этих отклонений. Кроме того, что именно вы хотите протестировать.
Но общая идея состоит в том, чтобы иметь только Черты , которые определяют API ваших сервисов, и значения по умолчанию (production) реализациииз тех. Если одной реализации требуется другая служба, она должна зависеть от нее.
Для тестирования вы создаете макеты зависимостей и тестируете реализацию службы.

Например:

// Toggleable.scala
trait Toggleable {
  def isToggleEnabled(name: String): Boolean
}

object Toggleable {
  final val impl: Toggleable =
    new Toggleable {
      override final def isToggleEnabled(name: String): Boolean =
        System.getProperty("name").toBoolea
    }
}

// Toggles.scala
trait Toggles {
  def isEnabled(name: String): Boolean =
}

object Toggles {
  final def impl(toggleable: Toggleable): Toggles =
    new Toggles {
      override final def isEnabled(name: String): Boolean =
        toggleable.isToggleEnabled(name)
    }
}

// Env.scala
trait Env {
  def doSomething(): Unit
}

object Env {
  final def prod(toggles: Toggles): Env =
    new Env {
      override final def doSomething(): Unit =
        println(toggles.isEnabled("toggle.name"))
    }
}

// Tests
object TestToggleable extends Toggleable {
  private val cache =
    scala.collection.mutable.HashMap.empty[String, Boolean]

  override final def isToggleEnabled(name: String): Boolean =
    cache.getOrElse(name, false)
}


class Tests {
  val toggles = Toggles.impl(toggleable = TestToggleable)
  assert(toggles.isEnabled("balh") == false)

  val env = Env.prod(toggles)
  prod.doSomething() // Not sure how do you test an Unit.
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...