Question

I have a class with method save():

public class FilesystemImagePersistenceStrategy implements ImagePersistenceStrategy {
    public void save(MultipartFile file, Image image) {
        File dest = createFile(image);
        writeToFile(file, dest);
    }

    // protected to allow spying
    protected void writeToFile(MultipartFile file, File dest) throws IOException {
        IOUtils.copy(file.getInputStream(), new FileOutputStream(dest));
    }
}

And now I want to check name of the file before it will be saved:

class FilesystemImagePersistenceStrategyTest extends Specification {
    private ImagePersistenceStrategy strategy = Spy(FilesystemImagePersistenceStrategy)
    private MultipartFile multipartFile = Mock()
    private Image image = TestObjects.createImage()

    def "save() should gives proper name to the file"() {
        given:
            String expectedFileName = ...
        when:
            strategy.save(multipartFile, image)
        then:
            1 * strategy.writeToFile({ MultipartFile file, File dest ->
                assert dest.name == expectedFileName
                return true
            })
    }
}

But unfortunately it doesn't work, real method invoked instead...

Why it so? And how to check method's argument?

P.S. I also provided an example at http://meetspock.appspot.com/script/5741031244955648


I ended up with the following:

    1 * strategy.writeToFile(
        multipartFile,
        { assert it.name == expectedFileName; return true }
    ) >> {}

There assert call needs to show me nice error message, like this:

it.name != expectedFileName
|  |    |  |
|  1.png|  2.png
|       false
/tmp/1.png

Also return true is required for case when test fails. Without this statement closure will return false and this method will not be accounted as candidate (and real method will be executed).

Was it helpful?

Solution

The argument constraint specified for 1 * strategy.writeToFile is wrong. There needs to be one constraint per argument, and constraints must not contain assert statements. Additionally, if a spy is to be used as a partial mock, the default response (which calls through to the real method) needs to be suppressed by supplying a stubbed response ("do nothing" in this case). This leads to:

...
then:
1 * strategy.writeToFile(multipartFile, { it.name == expectedFileName }) >> {}

Which can be simplified to:

...
then:
1 * strategy.writeToFile(multipartFile, expectedFileName) >> {}

OTHER TIPS

for multiplicity and assertions on mock, I used the following:

then:
numberOfCalls * mock.accept(_) >> {ArgType arg ->
    assert arg.id == my.id
    assert arg.minSerial == my.min()
    assert arg.maxSerial == my.max()
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top