1

我决定使用 File Factory 来模拟 File 对象的构造。

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 是一个经过检查的异常,它包装了被调用的方法或构造函数抛出的异常。”。伟大的。

有任何想法吗?

谢谢。

4

2 回答 2

3

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

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

于 2011-02-24T15:41:38.793 回答
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 动态添加的方法,方法是在您的自定义“包装器”类中覆盖它们。

于 2011-02-25T11:31:06.617 回答