Вопрос

I decided to use the File Factory to mock the File object construction.

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);
  }
}

And the test:

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(_)
  }

}

The thing is that it fails with NullPointerException!?

Using the debugger i got to:

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

And, verified, the "current file" really is a mocked file as specified in the test. "currentObject.objectContent" is not null, "currentFile" is not null.

All of a sudden, it jumps to BaseSpecRunner.java to this method:

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 is a checked exception that wraps an exception thrown by an invoked method or constructor.". Great.

Any ideas?

Thanks.

Это было полезно?

Решение 2

Peter, you were correct. The fixed test is here:

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
  }
}

However, i found this even more "in tune" with my previous testing methodology:

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(_)
  }
}

So, you can use Spock to mock the dynamically added methods by Groovy by overriding them in your custom "wrapper" class.

Другие советы

Spock doesn't currently support mocking methods like File.write() that are dynamically added by Groovy but aren't present in the class. You can solve this problem by using Groovy's MockFor instead, like you did for the other mock.

InvocationTargetException is an internal Groovy exception. It wraps the NullPointerException you are seeing. Spock is smart enough to unwrap the exception for you.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top