【问题标题】:Groovy mocking File factory with SpockGroovy 使用 Spock 模拟文件工厂
【发布时间】:2011-02-24 14:24:37
【问题描述】:

我决定使用文件工厂来模拟文件对象的构造。

class FileClass {

  def basePath
  def listObjects = []

  def createFilePerObject() {
    listObjects.each {currentObject ->
      // Use some other libraries for mocking(cant run PowerMockito, Gareth Davis adviced me to use JMockit)

      // File currentFile = new File("${basePath.toString()}/${currentObject.objectName}")

      //File factory(The simplest of the solutions)...
      File currentFile = FileFactory.makeFile("${basePath.toString()}/${currentObject.objectName}")

      currentFile.write currentObject.objectContent   //Verify this behaviour!!
    }
  }

}

class SimpleObject {
  String objectName
  String objectContent
}

//Really simple
class FileFactory {
  def static makeFile(String pathFileName) {
    return new File(pathFileName);
  }
}

还有测试:

class FileClassTest extends Specification {

  FileClass fileClass

  def "test simple object"() {

    def listObjects = []

    SimpleObject object1 = new SimpleObject(objectName: "First object", objectContent: "First object content")
    SimpleObject object2 = new SimpleObject(objectName: "Second object", objectContent: "Second object content")
    SimpleObject object3 = new SimpleObject(objectName: "Third object", objectContent: "Third object content")
    SimpleObject object4 = new SimpleObject(objectName: "Fourth object", objectContent: "Fourth object content")

    listObjects << object1
    listObjects << object2
    listObjects << object3
    listObjects << object4

    fileClass = new FileClass(listObjects: listObjects, basePath: ".")

    def mockFile = Mock(File)

    def mockFileFactory = new MockFor(FileFactory)
    mockFileFactory.demand.makeFile {mockFile}    //Return the mocked file...

    when:
    mockFileFactory.use {
      fileClass.createFilePerObject()
    }

    then:
    1 * mockFile.write(_)
  }

}

问题是它因 NullPointerException 而失败!?

使用我得到的调试器:

currentFile.write currentObject.objectContent   //Verify this behaviour!!

并且,经过验证,“当前文件”确实是测试中指定的模拟文件。 “currentObject.objectContent”不为空,“currentFile”不为空。

一下子就跳到BaseSpecRunner.java这个方法:

protected Object invokeRaw(Object target, MethodInfo method, Object[] arguments) {
    if (method.isStub()) return null;

    try {
      return method.getReflection().invoke(target, arguments);
    } catch (InvocationTargetException e) {
      //Here it fails!
      runStatus = supervisor.error(new ErrorInfo(method, e.getTargetException()));
      return null;
    } catch (Throwable t) {
      Error internalError =
          new InternalSpockError("Failed to invoke method '%s'", t).withArgs(method.getReflection().getName());
      runStatus = supervisor.error(new ErrorInfo(method, internalError));
      return null;
    }
  }

“InvocationTargetException 是一个检查过的异常,它包装了被调用的方法或构造函数抛出的异常。”。太好了。

有什么想法吗?

谢谢。

【问题讨论】:

    标签: java groovy mocking spock


    【解决方案1】:

    Spock 目前不支持像 File.write() 这样由 Groovy 动态添加但在类中不存在的模拟方法。您可以改用 Groovy 的 MockFor 来解决此问题,就像您对其他模拟所做的那样。

    InvocationTargetException 是一个内部 Groovy 异常。它包装了您所看到的NullPointerException。 Spock 足够聪明,可以为您解开异常。

    【讨论】:

      【解决方案2】:

      彼得,你是对的。 固定测试在这里:

      class FileClassTest extends Specification {
      
        FileClass fileClass
      
        def "test simple object"() {
      
          def listObjects = []
      
          SimpleObject object1 = new SimpleObject(objectName: "First object", objectContent: "First object content")
          SimpleObject object2 = new SimpleObject(objectName: "Second object", objectContent: "Second object content")
          SimpleObject object3 = new SimpleObject(objectName: "Third object", objectContent: "Third object content")
          SimpleObject object4 = new SimpleObject(objectName: "Fourth object", objectContent: "Fourth object content")
      
          listObjects << object1
          listObjects << object2
          listObjects << object3
          listObjects << object4
      
          fileClass = new FileClass(listObjects: listObjects, basePath: ".")
      
          def mockFile = new MockFor(File)
      
          //This is how i can verify that the write method was called 4 times(no more, no less)
          // but im not using Spock and some of the advanced verification abilites that come with it...
          mockFile.demand.write(4) {}
      
          def mockFileFactory = new MockFor(FileFactory)
          mockFileFactory.demand.makeFile(4) {new File(".")}    //Fixed.
      
          when:
          mockFile.use {
            mockFileFactory.use {
              fileClass.createFilePerObject()
            }
          }
          then:
      //    1 * mockFile.write(_)
      
          //Just pass... Verification is done by MockFor, not Spock
          true
        }
      }
      

      但是,我发现这与我之前的测试方法更加“一致”:

      class FileClassTest extends Specification {
      
        class FileCustomWrapper extends File {
      
          def FileCustomWrapper(String pathname) {
            super(pathname);
          }
      
          //Ovveride the DefaultGroovyMethods method, Spock can recognize it, its staticly written
          // , and ovveriden only for the use of mocking...
      
          def write(String s) {
            metaClass.write(s)
            //super.write(s)
          }
        }
      
        FileClass fileClass
      
        def "test simple object"() {
      
          def listObjects = []
      
          SimpleObject object1 = new SimpleObject(objectName: "First object", objectContent: "First object content")
          SimpleObject object2 = new SimpleObject(objectName: "Second object", objectContent: "Second object content")
          SimpleObject object3 = new SimpleObject(objectName: "Third object", objectContent: "Third object content")
          SimpleObject object4 = new SimpleObject(objectName: "Fourth object", objectContent: "Fourth object content")
      
          listObjects << object1
          listObjects << object2
          listObjects << object3
          listObjects << object4
      
          fileClass = new FileClass(listObjects: listObjects, basePath: ".")
      
          def mockFile = Mock(FileCustomWrapper)
      
          def mockFileFactory = new MockFor(FileFactory)
          mockFileFactory.demand.makeFile(4) {mockFile}     //Pass the Spock mock
      
          when:
          mockFileFactory.use {
            fileClass.createFilePerObject()
          }
      
          then:
          //You can use the verification...
          4 * mockFile.write(_)
        }
      }
      

      因此,您可以使用 Spock 来模拟 Groovy 动态添加的方法,方法是在您的自定义“包装器”类中覆盖它们。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-08-29
        • 1970-01-01
        • 2021-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多