Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Scala测试中分解对象功能_Scala_Unit Testing_Testing_Mocking - Fatal编程技术网

如何在Scala测试中分解对象功能

如何在Scala测试中分解对象功能,scala,unit-testing,testing,mocking,Scala,Unit Testing,Testing,Mocking,这是一个常见问题的一般问题,让我们尽可能简单地描述它。。。假设我有: class A { def x() = //do something with e def e() = //do something else } 基本上是一个简单的类,其中方法x()调用方法e()。x()和e()的逻辑相当复杂。因此,在集成中测试它们(通过调用x()并断言)是不实际的。单独测试x()和e()更容易,只需一些模拟。所以我可以去做: abstract class A { def x() = {

这是一个常见问题的一般问题,让我们尽可能简单地描述它。。。假设我有:

class A {
  def x() = //do something with e
  def e() = //do something else
}
基本上是一个简单的类,其中方法
x()
调用方法
e()
。x()和e()的逻辑相当复杂。因此,在集成中测试它们(通过调用
x()
并断言)是不实际的。单独测试
x()
e()
更容易,只需一些模拟。所以我可以去做:

abstract class A {
  def x() = {
    /* do */
  }

  def e()
}

object A {
  def eImpl() = {}

  def apply(): A = new A() {
    override def e(): Unit = eImpl()
  }
}
然后用以下方法测试x()的行为:


但是。。。这假设伴随对象中的()绑定了正确的实现。总的来说,这一切对我来说可能有点冗长。那么,有什么方法可以构建这个类以进行可靠的测试,并确保实例具有正确的实现呢?

您不是在寻找间谍吗

假定

class A {
    def x(): String = "Hello" + e()
    def e(): String = "World"
}
您可以分别模拟
e()
x()

import org.mockito.Mockito._
import org.scalatest.mockito.MockitoSugar
import org.scalatest.{Matchers, WordSpec}
class ASpec extends WordSpec with Matchers with MockitoSugar {
    "Spy test" should {
        "mock only e" in {
            val a = spy(new A())
            when(a.e()).thenReturn("Mock")
            a.x() shouldBe "HelloMock"
            a.e() shouldBe "Mock"
        }
        "mock only x" in {
            val a = spy(new A())
            when(a.x()).thenReturn("Yo" + a.e())
            a.x() shouldBe "YoWorld"
            a.e() shouldBe "World"
        }
    }
}

通过使用traits,您可以在不引入模拟框架的情况下实现这一点

trait A {
  def x() = "Hello " + e()
  def e() = "World"
}

class TestAX extends A {
  override def x() = super.x()
  override def e() = "Mock"
}

class TestAE extends A {
  override def x() = "Mock " + e()
  override def e() = super.e()
}

"x in A" should "do this and that" {
  val testA = new TestAX()
  assert(testA.x() == "Hello Mock")
  assert(testA.e() == "Mock")
}

"e in A" should "do something else" in {
  val testA = new TestAE()
  assert(testA.x() == "Mock World")
  assert(testA.e() == "World")
}
TestAX使用生产版本的x()和重写e(),或者不执行任何操作,或者提供测试使用的信息(可以是常量值或其他内容)


这就是说,如果e()和x()都相当复杂,我建议将它们分解为更小的部分,这样您就不会测试“完成所有工作->完成所有工作了吗?”

是的,spy就是这么做的,它还证明了x在调用a。我注意到,如果e的定义类似于def e()=???当在spy对象中模拟它时,mockito会在when(mock.e())调用中抛出NPE。问题是要确保x实际上在调用e,所以我能够独立地测试它们。您的解决方案不能证明x正在调用e。它允许单独的测试,但x确实可以错误地调用重构时留下的另一个方法e1(),而您不知道。另外,为什么需要声明两个类?通过在new TraitAX{…}中的“do something”中使用两个匿名实现也可以达到同样的效果。我声明了两个类对于其他读者来说是显式的,它们可以是匿名子类。我的观点是,您可以在不引入新的测试依赖项的情况下完成在公认答案中所做的事情。而不是
{}
,使用一些常量值(如
“Mock”
)实现未被测试的函数,并且断言与接受答案中的断言相同。我将更新我的答案,使之更清晰。你的答案错误的原因实际上有两个原因:1)如果你呼叫super,则无需覆盖。2) 您的解决方案不能证明,如果x()使用e(),那么x实际上是在调用e(),这就是问题中提出的问题。您的测试将不能这么说。此外,模拟并不一定意味着引入测试依赖项,可以使用临时类来完成。这只是一项测试技术。
trait A {
  def x() = "Hello " + e()
  def e() = "World"
}

class TestAX extends A {
  override def x() = super.x()
  override def e() = "Mock"
}

class TestAE extends A {
  override def x() = "Mock " + e()
  override def e() = super.e()
}

"x in A" should "do this and that" {
  val testA = new TestAX()
  assert(testA.x() == "Hello Mock")
  assert(testA.e() == "Mock")
}

"e in A" should "do something else" in {
  val testA = new TestAE()
  assert(testA.x() == "Mock World")
  assert(testA.e() == "World")
}