当类依赖于隐式时,是否有惯用的方法将类转换为对象?

时间:2019-04-23 20:24:47

标签: scala singleton implicit scalatest

我是Scala的新手。

我有一系列执行UI测试的测试类,以及一系列包含可重用的辅助方法的类。

示例测试类:

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转换为object

但是,我唯一能弄清楚该怎么做的方法是在每个方法中添加一个implicit WebDriver参数。可以,但是很难看。还有其他更简洁的方法可以实现我想要的功能吗?或者,是否有一种更惯用的方式来完全组织这种“测试类/帮助方法”关系?

4 个答案:

答案 0 :(得分:2)

可以将帮助类更改为对象,并仍然为成员方法提供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 :(得分:1)

考虑使用这样的抽象隐式驱动程序字段将MyCoolPage变成特征

trait MyCoolPage {
  implicit val driver: WebDriver

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

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

然后将MyCoolTestClass扩展MyCoolPage并使用自定义配置的驱动程序覆盖driver字段,如下所示

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

  //Tests below rely on myCoolPage methods
}

现在MyCoolTestClass可以访问MyCoolPage中的每个方法,并且这些方法不需要隐式驱动程序参数。

答案 2 :(得分:1)

我认为,没有惯用的方法,可以按照您的要求将class转换为object。这是我对原因的看法:

您在driver中声明了一个属性MyCoolClass,您正在这些方法中使用它。因此,您的class实际上具有状态。 WebDriverstate injectedMyCoolPage中。根据注入的WebDriver的实现,方法调用的处理方式可能会有所不同。

要克服这一点,您需要将WebDriver作为每个方法的隐式参数,这是您自己发现的。但是,这将允许在运行时从外部替换驱动程序。这会违反Open-Closed-Principle,我认为它甚至比使用您的class构造还要惯用

如果以后再次遇到此问题,则可以尝试在不使用implicit的情况下编写代码。如果将class更改为object似乎仍然是一种适当的方法,那么您可能会很高兴。

答案 3 :(得分:0)

  

现在,所有帮助程序类实际上都不具有可变状态。这使我想将它们从class转换为object

但是他们确实有状态。是的,它是不可变的,但是大多数案例类的状态OptionList也是如此……它们都不应该转换为object。我认为没有一种解决方案真的比您开始使用的解决方案更好。

但是,这里有一个选择:将帮助器对象嵌套在超类中:

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的加载是延迟的,因此如果MyCoolTestClass不使用MyCoolPage2,则不会为此付费。折衷是所有帮助程序类都必须在一个文件中定义。