有没有办法用一个函数来模拟一个类`IntToString extends(Int => String)`?

时间:2014-08-26 14:03:56

标签: scala mocking

我定义了一个扩展函数类型的类:

class Action1 extends (String => String) {
  def apply(s:String) = s + "!"
}

它可以用于其他方法:

class Action2(action1: Action1) extends (String => String) {
  def apply(n:String) = {
     val all = (action1 andThen toUpper)
     all(n)
  }
  private def toUpper(s:String) = s.toUpperCase
}

我可以使用模拟编写Action2的单元测试:

 val action1 = mock[Action1]
 action1.apply(anyString) returns "sss"
 action2.andThen(any) returns ???      // I don't want to mock this

 val action2 = new Action2(action1)
 val result = action2("aa")
 result must beEqual("SSS")

你可以看到我需要模拟applyandThen,这有点无聊。

如果我可以使用函数来模拟action1,那就太棒了,比如:

 val action2 = new Action2(_ => "sss")
 val result = action2("aa")
 result must beEqual("SSS")

未编译,因为_ => "sss"无法转换为Action1。如果我将Action2更改为:

class Action2(action1: String => String) extends (String => String)

它将被编译,但我将失去Action1类型的提示。

如果有任何方法可以保持类型Action1,还有像_ => "sss"这样的简单函数来进行模拟吗?

1 个答案:

答案 0 :(得分:0)

问题是Action1是Function1的子类,因此以下内容无法编译:

val f: (String => String) = ...
val a: Action1 = f                  // Does not compile

除了真正的Action1Action2之外,您还有两个选择:

  1. 使用代码生成工具覆盖apply
  2. Action1
  3. 更改Action2
  4. 的构造函数

    对于选项1,您可以使用Mockito' s spy feature

    val action1 = new Action1
    val spy = Mockito.spy(action1)
    Mockito.when(spy.apply(org.mockito.Matchers.anyString)).thenReturn("sss")
    
    val action2 = new Action2(spy)
    val result = action2("aa")
    result must beEqual("SSS")
    

    对于选项2,您可以接受String => String

    class Action2(action1: String => String) extends (String => String) { ... }
    

    可能有别名:

    type Action = (String => String)
    
    class Action1 extends Action {
      def apply(s:String) = s + "!"
    }
    
    class Action2(action1: Action) extends Action {
      def apply(n:String) = {
        val all = (action1 andThen toUpper)
        all(n)
      }
      private def toUpper(s:String) = s.toUpperCase
    }
    
    val action2 = new Action2((s: String) => "sss")
    val result = action2("aa")
    result must beEqual("SSS")
    

    或引入特征:

    trait Action extends (String => String)
    
    class Action1 extends Action { ... same as above ... }
    
    class Action2(action1: Action) extends Action { ... same as above ...}
    
    def mockedAction1(r: String) = new Action { def apply(s:String) = r }
    
    val action2 = new Action2(mockedAction1("sss"))
    val result = action2("aa")
    result must beEqual("SSS")