代码之家  ›  专栏  ›  技术社区  ›  Aaron Novstrup

如何用scala中的函数参数模拟方法?

  •  19
  • Aaron Novstrup  · 技术社区  · 16 年前

    我试图模拟一个接受按名称调用参数的方法调用:

    import org.scalatest.WordSpec
    import org.scalatest.mock.MockitoSugar
    import org.mockito.Mockito._
    import org.junit.runner.RunWith
    import org.scalatest.junit.JUnitRunner
    
    trait Collaborator {
       def doSomething(t: => Thing)
    }
    
    trait Thing
    
    @RunWith(classOf[JUnitRunner])
    class Test extends WordSpec with MockitoSugar {
       "The subject under test" should {
          "call the collaborator" in {
             // setup
             val m = mock[Collaborator]
             val t = mock[Thing]
    
             // test code: this would actually be invoked by the SUT
             m.doSomething(t)
    
             // verify the call
             verify(m).doSomething(t)
          }
       }
    }
    

    我主要对Mockito感兴趣,因为这正是我使用的工具,但我想看看是否有任何一个主要的模拟框架能够进行这种测试。测试在运行时在 verify 行,错误如下

    Argument(s) are different! Wanted:  
    collaborator.doSomething(  
       ($anonfun$apply$3) <function>  
    );  
    -> at Test$$anonfun$1$$anonfun$apply$1.apply(Test.scala:27)  
    Actual invocation has different arguments:  
    collaborator.doSomething(  
        ($anonfun$apply$2) <function>  
    );  
    -> at Test$$anonfun$1$$anonfun$apply$1.apply(Test.scala:24)  
    

    如果我正确理解这种情况,编译器将隐式包装 t 在返回的空函数中 T . 然后,模拟框架将该函数与测试代码中生成的函数进行比较,后者是等效的,但不是 equals() .

    我的例子是这个问题的一个相对简单的版本,但是我认为这对于任何高阶函数都是一个问题。

    3 回复  |  直到 13 年前
        1
  •  9
  •   Alexey    16 年前

    这看起来很难看,但希望它能帮助您找到好的解决方案:

    import org.scalatest.mock.MockitoSugar
    import org.mockito.Mockito._
    
    trait Collaborator {
       def doSomething(t: => Thing)
    }
    
    trait Thing
    
    new MockitoSugar {
         // setup
         val m = mock[Collaborator]
         val t = mock[Thing]
    
         m.doSomething(t)
    
         classOf[Collaborator].getMethod("doSomething", classOf[Function0[_]]).invoke(
            verify(m), 
            new Function0[Thing] { 
                def apply() = null
                override def equals(o: Any): Boolean = t == o.asInstanceOf[Function0[Thing]].apply() 
          })
    }
    
        2
  •  1
  •   miaubiz    16 年前

    此问题似乎特定于按名称调用,因为在常规的高阶函数中,可以与显式函数x对象匹配:

    verify(collabor).somethingelse(any(function2[string,thing]))

    在按名称的情况下,将参数包装成函数0是隐式完成的,Alexey的回答显示了如何使用显式参数调用模拟。

    你可以写一些类似于你自己的验证的东西,它将应用mockito捕获的论点。

    mockito内部记录调用及其参数,例如: http://code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java

        3
  •  0
  •   Eric    13 年前

    你可以试试 specs2 . 在规格2中,我们“劫持”莫基托 Invocation 用于ByName参数的类:

    trait ByName { def call(i: =>Int) = i }
    val byname = mock[ByName]
    
    byname.call(10)
    there was one(byname).call(10)