I put this in my Spock test:
GroovyMock( File, global: true)
File.createNewFile() >> null
... which I realise is unorthodox/silly/curious: createNewFile
is a non-static method.
The code involved is like this:
if( indexInfoFile.createNewFile() ) {
... it turns out from my experiements that mocking createNewFile
like this always returns false
, even if you try putting a block in the mock:
GroovyMock( File, global: true)
File.createNewFile() >> {
log.info( 'Hello mum!')
}
... the log message is not printed but createNewFile
again returns false
.
This is actually what I wanted (i.e. to mock a false
return from createNewFile
).
Is this intentional, documented behaviour?
PS Caveat: from my experience/experiments today, there is no doubt that this mock method does replace all occurrences of an invocation of this method, on any File
instance. However, it appears also to have some alarming side-effects too: for example, a directory I created in my given
block before the 2 GroovyMock
lines is found NOT to exist afterwards, still in the given
block, when I went
myDirPath.toFile().exists()
... I assume this is because toFile
involves an invocation of createNewFile
...
As documented, Groovy mocks only have additional "magic" when used with Groovy classes, but I assume that you are trying to mock java.io.File
, which is a Java JRE class. Thus, the Groovy mock will behave like a normal Spock mock. So I don't know why you want to use the Groovy mock in the first place - maybe because you want to use the global: true
feature in order to avoid refactoring for testability in your application class.
As you do not show us an MCVE, I have no way of knowing whether indexInfoFile
can be injected into your class/method under test or if it is a dependency created inside the method. In the latter case you need to refactor, it is as simple as that. Dependencies should be injectable, period.
As for your code snippets, there are a few things wrong with them:
File.createNewFile()
returns boolean
, so it does not make any sense to stub it to return null
.false
, null
or 0
, depending on their return type. So there is no need to stub the result for createNewFile()
in the first place if you want it to return false
because it already does.Now, assuming your class under test looks like this (already prepared or refactored for dependency injection via method argument, constructor argument or setter)...
package de.scrum_master.stackoverflow.q59842227;
import java.io.File;
import java.io.IOException;
import java.util.Random;
public class FileCreator {
private static final Random RANDOM = new Random();
public boolean createIndexInfoFile(File indexInfoFile) throws IOException {
if (indexInfoFile.createNewFile()) {
System.out.println("File \"" + indexInfoFile + "\" created");
return true;
}
System.out.println("File \"" + indexInfoFile + "\" NOT created");
return false;
}
public static void main(String[] args) throws IOException {
new FileCreator().createIndexInfoFile(
new File("_abc_" + RANDOM.nextInt(10000) + ".txt")
);
}
}
... then you can test it like this:
package de.scrum_master.stackoverflow.q59842227
import spock.lang.Specification
class FileCreatorTest extends Specification {
def "index info file created"() {
given:
File file = Mock() {
createNewFile() >> true
}
expect:
new FileCreator().createIndexInfoFile(file)
}
def "no index info file created"() {
given:
File file = Mock()
expect:
!new FileCreator().createIndexInfoFile(file)
}
}
See? There is no need for global or Groovy mocks, normal mocks will do just fine. But you need to make your code testable instead of using fancy tricks.