Есть ли идиоматический способ преобразовать класс в объект, когда класс полагается на неявный? - PullRequest
2 голосов
/ 23 апреля 2019

Я новичок в Скале.

У меня есть серия тестовых классов, которые выполняют тесты пользовательского интерфейса, и серия классов, которые содержат вспомогательные методы многократного использования.

Пример тестового класса:

class MyCoolTestClass extends FreeSpec {

    //Note:  This driver needs to be configurable by the test in some way.
    implicit val driver:WebDriver = new ChromeDriver()

    val myCoolPage = new MyCoolPage

    //Tests below rely on myCoolPage methods
}

Пример класса помощника:

class MyCoolPage(implicit driver:WebDriver) {
    def clickElement1(){
     //relies on driver
    }
    def assertElement2Enabled(){
     //relies on driver
    }
}

Теперь ни один из вспомогательных классов на самом деле не имеет изменяемого состояния. Это заставляет меня хотеть преобразовать их из class es в object s.

Однако единственный способ выяснить, как это сделать, - добавить аргумент implicit WebDriver к каждому методу. Это работает, но это некрасиво. Есть ли еще лаконичные способы добиться того, чего я хочу здесь? В качестве альтернативы, есть ли более идиоматический способ полностью организовать эти отношения Test Class / Helper Method?

Ответы [ 4 ]

2 голосов
/ 24 апреля 2019

Вы можете изменить классы справки на объекты и по-прежнему предоставлять значение implicit для методов-членов.

object MyCoolPage {

  private val driver :WebDriver = implicitly[WebDriver]

  def clickElement1() = ???          //relies on driver
  def assertElement2Enabled() = ???  //relies on driver
}

Но тогда объявление implicit должно выйти изтестовый класс.На ум приходят две возможности: объект WebDriver ...

object WebDriver {
  implicit val wd :WebDriver = new ChromeDriver()
  ...

... или выделенный объект.

object MyCoolPage {
  import MyTestImplicits._
  private val driver :WebDriver = implicitly[WebDriver]
  ...

В общем, я не уверенэто будет стоить усилий.

1 голос
/ 24 апреля 2019

Я бы сказал, что нет идиоматического способа для преобразования class es в object s, как вы и просите.И вот моя точка зрения на то, почему:

Вы объявили атрибут driver в MyCoolClass, который вы используете в методах.Следовательно, ваше class действительно имеет состояние.WebDriver - это состояние, введенное в MyCoolPage.В зависимости от реализации внедренного WebDriver способ обработки вызовов методов может различаться.

Чтобы преодолеть это, вам нужно дать WebDriver в качестве неявного аргумента для каждого из ваших методов, какВы узнали сами.Однако это позволит внешне заменить драйвер во время выполнения.Это нарушит Open-Closed-Principle , который, я бы сказал, даже на 1020 * менее идиоматичен , чем при использовании вашей конструкции class.

Если вы столкнетесь с этой проблемойснова в будущем вы можете попытаться написать код без использования implicit.Если это все еще кажется подходящим способом изменить class на object, вам, вероятно, хорошо идти.

1 голос
/ 24 апреля 2019

Рассмотрите возможность превращения MyCoolPage в черту с абстрактным неявным полем драйвера, например,

trait MyCoolPage {
  implicit val driver: WebDriver

  def clickElement1() = {
    //relies on driver
  }

  def assertElement2Enabled(){
    //relies on driver
  }
}

Затем задайте MyCoolTestClass extension MyCoolPage и переопределите поле driver с настроенным пользователем драйвером следующим образом

class MyCoolTestClass extends FreeSpec with MyCoolPage {
  override implicit val driver: WebDriver = new ChromeDriver()

  //Tests below rely on myCoolPage methods
}

Теперь MyCoolTestClass имеет доступ к каждому методу из MyCoolPage, и этим методам не требуется неявный параметр драйвера.

0 голосов
/ 24 апреля 2019

Теперь ни один из вспомогательных классов на самом деле не имеет изменяемого состояния. Это заставляет меня хотеть конвертировать их из class es в object s.

Но у них есть состояние. Да, он неизменный, но так же, как и состояние большинства классов дел, Option s, List s ... Ни один из них не должен быть преобразован в object s. Я не думаю, что есть решение, которое действительно лучше, чем то, с которого вы начали.

Однако есть вариант: вложение вспомогательных объектов в суперкласс:

abstract class AbstractTestClass extends FreeSpec {
  // may optionally be implicit or non-abstract
  val driver: WebDriver 

  object MyCoolPage {
    def clickElement1(){
      //relies on driver
    }
    def assertElement2Enabled(){
      //relies on driver
    }
  }

  object MyCoolPage2 ...
}

class MyCoolTestClass extends AbstractTestClass {
  override val driver: WebDriver = new ChromeDriver()

  // can use MyCoolPage methods
}

Обратите внимание, что object s загружаются лениво, поэтому, если MyCoolTestClass не использует MyCoolPage2, оно не будет платить за это. Компромисс заключается в том, что все вспомогательные классы должны быть определены в одном файле.

...