0

Okay, please consider the following method. Let me first tell you that my goals of the method is to determine if file system assets exist. That's to say this is an internal company site where you'd expect the base directory to already be there on the installed server and the same with the parameter passed directory and filename. The end user of the web app will have ALREADY placed the assets in place.

What this method tests is that everything is working.

BUT, how do I TEST it? I don't want to test that the JAVA.NIO classes work, I know they will. I want to test that given a set of inputs, my behaviors are what I think they are.

How would you do this?

@Component
public class FilePathTestService {

    @Value("${userlists.basedirectory}")
    private String userListDirectory;

    public FilePathTestResult testFilePath(FilePathTestRequest filePathTestRequest) {

            if (StringUtils.isBlank(userListDirectory))         
            {
                return  (new FilePathTestResult(
                    false, 
                    "ERROR: Base userListDirectory is Blank/Null"));
            }

            try {
                Path pathToDirectory = Paths.get(
                    userListDirectory, 
                    filePathTestRequest.getPathName());

                if (!Files.isDirectory(pathToDirectory))
                {
                    return  (new FilePathTestResult(
                        false, 
                        "ERROR: Directory could not be found for this path."));
                }

                Path pathToFile = Paths.get(
                    userListDirectory, 
                    filePathTestRequest.getPathName(), 
                    filePathTestRequest.getFileName());

                if (Files.notExists(pathToFile))
                {
                    return  (new FilePathTestResult(
                        false, 
                        "ERROR: File could not be found, but directory was found."));
                }   
                else {
                    File fileInfo = pathToFile.toFile();
                    FilePathTestResult result = new FilePathTestResult(true);
                    result.setModifyDate(LocalDateTime.ofInstant(Instant.ofEpochMilli(
                        fileInfo.lastModified()), 
                        ZoneId.systemDefault()));
                    return (result);
                }

            } catch (InvalidPathException e) {  
                return  (new FilePathTestResult(
                    false, 
                    "ERROR: This path string cannot be converted to a Path"));
        }                       
    }   
}
  • To help with request for test design, add some specific sample data Input, with expected results. In general, though, you'd want a test case for each if() and else(). This isn't checking the NIO (though is can appear that way), but rather that you are returning the FilePathTestResult you intend to. – Kristian H Oct 15 '16 at 4:58
1

Looks pretty straightforward, no need to split this up if you don't like to. Provide a way to set userListDirectory directly, without the need for a Spring configuration (for example, add a constructor for FilePathTestService where you can pass userListDirectory as a parameter).

Then you can easily write tests with different combinations of userListDirectory and filePathTestRequest data as input, where you pass names of existing files and folders, non-existing files in existing folders, or a non-existing folder (of course, you should provide some files and folders as test data for this purpose). Each of your test can validate if the returned FilePathTestResult object contains what you expect it to contain.

You can make the test a little bit more robust if you extend your FilePathTestResult object by a machine readable enum indicating the exact failure type. That way, you can assert that the function returns the expected type, instead of comparing the results with the returned error string. Error messages might be subject to change in the future, error type codes tend not to change so frequently.

0

Doc's answer is very good (especially being able to set things directly) and will work fine if you want to touch the filesystem for your tests. There is nothing wrong with doing that, but some people prefer not to do this as they don't consider this to be a unittest anymore (there are varying opinions as to whether or not unittests should touch databases, filesystems and the likes which I won't get into). Just keep in mind that testing with an actual filesystem will incur some penalty to the speed at which you run your tests.

Alternatively, the approach I take to these things is wrapping all I/O operations in a simple class that implements an interface (e.g FileSystem or some variation). This class simply passes through to the existing File-operations and contains no logic whatsoever. That allows me to mock up the FileSystem-interface which allows for unittests that do not touch the filesystem.

The wrapping class itself is not tested via a unittest because it contains no actual logic (it is a simple pass-through) and some degree of testing on it is performed by integrationtests anyway.

  • One useful approach here: use the java.nio.file.FileSystem abstract class for your file accesses and github.com/google/jimfs to provide an implementation that doesn't need to touch actual files. – Periata Breatta Sep 15 '16 at 21:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.