pfh
pfh

Reputation: 530

Mocking groovy new File.eachFile()

I'm having trouble mocking file.eachFile() {}.

I'm trying to mock the files that get returned from file.eachFile{} - i don't want to actually test the contents of a real directory on my drive.

I don't see how i can achive this using Spock(as i understand it's used more for mock verification).

So, i decided to use the "native" Groovy mock capabilities, and the MOP capabilities. No luck.

For example(i had to remove the File type in order to pass the object to the testing funcion):

def mockFile = new MockFor(java.io.File)

mockFile.demand.eachFile {[new File("script1.sql"), new File("script2.syn"), new File("script3.grt")].each {it}} 

Or(using metaclass):

File mockFile = new File("irrelevant_path")
mockFile.metaClass.eachFile = {[new File("script1.sql"), new File("script2.syn"), new File("script3.grt")].each {it}}

I eachFile method is defined as:

public static void eachFile(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException {
        eachFile(self, FileType.ANY, closure);
    }

I'm already a litty bit fuzzy from all the coding attempts to mock this :)

I managed to reach the method(no runtime exceptions), but am unable to actually see the injection of the File list as defined beforehand.

The tested method is(for simplicity):

folderSourceScripts.eachFile {File currentFile -> ..... }

In Java, Mockito would do this in a minute, but i read somewhere that there were some issues regarding integration with Groovy. There is a problem when mocking the classes because Groovy makes a call to the Metaclass.

I saw this here.

tim_yates wrote:

def mock = new MockFor(File)
mock.demand.eachFile {
  [new File("script1.sql"), new File("script2.syn"), new File("script3.grt")].each { (it) }
}

mock.use {
  new File('.').eachFile {file ->
    println "$file"
  }

The script works, but there's a catch when using it in the test.

As i mentioned, i'm using Spock and the file variable is passed in a method. So using it the way you wrote it causes:

groovy.lang.MissingMethodException: No signature of method: groovy.mock.interceptor.MockFor.eachFile() is applicable for argument types: ... 

I'm guessing thats because there isnt direct usage of File.eachFile() in the code(it doesn't pass the mock to the method, it expects the mock to be instantiated in the "use" block)?

The code in the test looks like this:

when:
    mock.use {
      folderValidator.validate(mock)
    }

then:
    folderValidator.listMissingFiles.size == 3

The problem with proxyInstance(), which should pass the mock into the method is, as the example will show:

when:
    folderValidator.validate(mockProxy)

then:
    folderValidator.listMissingFiles.size == 3

When the mock is passed, groovy can't instantiate the class, because it doesn't know which constructor to use?

org.codehaus.groovy.runtime.metaclass.MethodSelectionException: Could not find which method <init>() to invoke from this list:

  private java.io.File#<init>(java.lang.String, int)
  private java.io.File#<init>(java.lang.String, java.io.File)
  public  java.io.File#<init>(java.lang.String)
  public  java.io.File#<init>(java.net.URI)
  public  java.io.File#<init>(java.io.File, java.lang.String)
  public  java.io.File#<init>(java.lang.String, java.lang.String)

In theory it does the work, but in this example it fails, maybe I'm coding it wrong. I started using Groovy two weeks ago, so im still getting used to it. Thanks

Upvotes: 1

Views: 4998

Answers (1)

pfh
pfh

Reputation: 530

tim_yates wrote:

def mock = new MockFor(File)
mock.demand.eachFile {
  [new File("script1.sql"), new File("script2.syn"), new File("script3.grt")].each { (it) }
}

mock.use {
  new File('.').eachFile {file ->
    println "$file"
  }

He was right. As I said, I didn't use Groovy that long(neither Spock).

"I'm guessing thats because there isnt direct usage of File.eachFile() in the code(it doesn't pass the mock to the method, it expects the mock to be instantiated in the "use" block)?"

The code in the test looks like this:

when:

        mock.use {
          folderValidator.validate(new File("."))
        }

then:
        folderValidator.listMissingFiles.size == 3

The solution was to simply instantiate the mocked class inside the use block. I thought that the mock is instantiated and would be passed in the method - the way Mockito works.

Mockito:

File pathScripts;
pathScripts = mock(File.class);
when(pathScripts.list()).thenReturn(listFiles);

someObject.someTestingMethod(pathScripts)

The proxyInstance way would be to mock the class, BUT WITH PROPER CONSTRUCTOR ARGUMENTS!

def mock = new MockFor(File)
mock.demand.eachFile {func ->
  [new File("script1.sql"), new File("script2.syn"), new File("script3.grt")].each { func(it) }
}

def mockProxy = mock.proxyInstance(".")

mockProxy.eachFile {file ->
  println "$file"
}

And, the biggest mistake was the missing closure in my mocked method:

mockFile.demand.eachFile { **??** [new File("script1.sql"), new File("script2.syn"), new File("script3.grt")].each {**?**(it)}} 

So, thank you, tim_yates.

Upvotes: 2

Related Questions