I have the following short code snippet which I try to do unit testing on it via mockito
public String getExecutable()
{
String result = executable;
String ex = !hasExtension() ? executable + ".bat" : executable;
File f = new File( dir, ex );
if ( f.isFile() )
{
result = ex;
}
return result;
}
The dir is an instance of the class File which is been given via constructor to the class so no problem. Only this line:
File f = new File( dir, ex );
if ( f.isFile() ) {
..
}
So does exist any chance to mock out this via Mockito to make some tests on it so i can control the result of isFile()? Any idea?
It looks like dir is a member variable for the class containing getExecutable()? You could abstract dir into something that may contain files:
class FileContainer {
private final File dir;
public FileContainer(File aDir) { dir = aDir; }
public boolean contains(String aFile) {
return new File(dir, aFile).isFile();
}
}
Have your class hold one of these FileContainer objects, and use its contains() function to test for files. Arrange to inject a mock version of the FileContainer for testing. The mock version would override contains() and return whatever you want.
One idea is to extract new File( dir, ex ) to a new protected method and overwrite it during the test to return a mock.
public class YourClass
{
// ...
public String getExecutable()
{
String result = executable;
String ex = !hasExtension() ? executable + ".bat" : executable;
File f = createFile( dir, ex );
if ( f.isFile() )
{
result = ex;
}
return result;
}
#VisibleForTesting
protected File createFile( String ex, String dir )
{
return new File( dir, ex );
}
}
Before executing the test:
#Test
public void shouldReturnExecutableFile()
{
YourClass subject = new YourClass()
{
#Override
protected File createFile( String ex, String dir )
{
// return a mock for File
}
};
}
It is one of the techniques presented in Working Effectively with Legacy Code by Michael Feathers.
Related
Needing to scan an entire computer for files, I would like to start a Files.walkFileTree( startingFolder, FileVisitor) on each physical drive on a computer. However, I am finding the the FileVisitor override methods are getting confused. If I start only one thread, every things works fine, but when I start the second, or more, threads, the FileVisitor methods for the first thread are being ignored or being called with an incorrect file. The Files.walkFileTree() is a static method. I specify a new FileVisitor implementation for each thread, but to no avail. I find that the Files.walkFileTree() is about 65% faster than other methods. How can I make this multi-Threaded?
Happy Holidays and Thank you.
Peter Jerkewitz
Sample Code:
ArrayList<Path> seeds = new ArrayList<>(); // This is where we are going to start looking
private void launchTreeWalkers() {
ArrayList<Path> effectivelyFinalPath = this.seeds; // Physical drives on the computer ie C:\ D:\ E:\ ...
int[] index = new int[1]; // Effectively Final
this.twThreads = new Thread[ effectivelyFinalPath.size() ]; // TreeWalkerThreads
for ( int i=0; i < effectivelyFinalPath.size(); i++ ) {
index[i] = i;
this.twThreads[i] = new Thread( new Runnable() {
#Override
public void run() {
try {
tTreeWalker2( effectivelyFinalPath.get( index[0]) );
} catch ( Exception ex ) {
ex.printStackTrace();
}
}
}
);
this.twThreads[i].setDaemon( true ); // So the thread will die when the parent dies.
this.twThreads[i].setName("Tree Walker");
this.twThreads[i].start();
}
Display.fmt("%1$,d Tree Walker thread%2$s %3$s been started.", box( this.twThreads.length ), Aide.plural(this.twThreads.length), Aide.hasHave( this.twThreads.length ) );
}
/** A walkFileTree thread */
void tTreeWalker( Path startingPath ) throws Exception {
#SuppressWarnings("resource") // java.lang.UnsupportedOperationException when closing FileSystem
FileSystem fileSystem = FileSystems.getDefault();
Files.walkFileTree( fileSystem.getPath( startingPath.toString() ), new FileVisitorImpl());
}
/** */
public class FileVisitorImpl implements FileVisitor<Path> {
#Override // Called before a directory visit.
public FileVisitResult preVisitDirectory( Path directory, BasicFileAttributes attrs ) throws IOException {
Locate.this.folderCountLA.increment(); // Long Adder
if ( isMatch( excludeFolders, directory ))
return FileVisitResult.SKIP_SUBTREE;
Locate.this.foldersSelectedLA.increment();
return FileVisitResult.CONTINUE;
}
#Override // Called after a directory visit is complete.
public FileVisitResult postVisitDirectory( Path dir, IOException exc ) throws IOException {
return FileVisitResult.CONTINUE;
}
#Override // This method is called for each file visited. The basic attributes of the files are also available.
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Locate.this.fileCountLA.increment();
if ( ! isMatch( excludeFiles, file ) ) {
FileOfInterest foi = new FileOfInterest( file );
try {
if (( probZips ) && ( foi.isCompressedFile() )) {
foundFilesQ.put( foi );
//TODO Zip handler goes here
} else if ( isMatch( selectFiles, file ) ) {
Locate.this.filesSelectedLA.increment();
foundFilesQ.put( foi ); // Blocking if the Q is full.
}
} catch ( InterruptedException ex ) { // We do not expect this to ever happen.
ex.printStackTrace();
}
}
return FileVisitResult.CONTINUE;
}
#Override // If the file visit fails for any reason, the visitFileFailed method is called.
public FileVisitResult visitFileFailed( Path file, IOException exc) throws IOException {
if ( ! isMatch( knownFailures, file ) ) { // Ignore known failures
String msg = exc.toString();
if ( msg.contains( "java.nio.file.AccessDeniedException" )) {
Locate.this.accessDeniedLA.increment();
if ( showAccessDenied )
Display.fmt("visitFileFailed %s", exc.toString() );
} else {
Locate.this.failuresLA.increment();
Display.fmt("visitFileFailed %s", exc.toString() );
}
}
return FileVisitResult.CONTINUE;
}
} // End of FileVisitorImpl
Problem Solved. In trying to fool java by creating Effectively Final variables as arrays to use in the creation of the runnable was biting me. As I was creating 4 threads in my test case, all 4 threads were running on the same drive, the last one entered. They were not getting used in the order, and at the time, in which I created the runnable. I solved the problem by letting the thread access an AtomicInteger to index the array containing the drives. I also created the FileVisitorImpl in the thread.
Thanks and Happy New Year.
Peter Jerkewitz
I'm currently developing an Eclipse Neon editor plug-in. At the moment I'm trying to be able to open files from the filesystem, which weren't created inside of Eclipse. To accomplish that, I need to get an IProject in the following method:
public static IProject getProject(IStorage storage) {
if (storage instanceof IFile) {
return ((IFile) storage).getProject();
}
else if (storage instanceof IJarEntryResource) {
return ((IJarEntryResource) storage).getPackageFragmentRoot().getJavaProject().getProject();
}
else if (storage instanceof FileStorage) {
// ????
}
else {
throw new IllegalArgumentException("Unknown IStorage implementation");
}
}
Here FileStorage is an own implementation of the IStorage interface. And looks like that:
public class FileStorage implements IStorage {
private final FileStoreEditorInput editorInput;
FileStorage( FileStoreEditorInput editorInput ) {
this.editorInput = editorInput;
}
#Override
public <T> T getAdapter( Class<T> adapter ) {
return Platform.getAdapterManager().getAdapter( this, adapter );
}
#Override
public boolean isReadOnly() {
return false;
}
#Override
public String getName() {
return editorInput.getName();
}
#Override
public IPath getFullPath() {
return new Path( URIUtil.toFile( editorInput.getURI() ).getAbsolutePath() );
}
#Override
public InputStream getContents() {
try {
return editorInput.getURI().toURL().openStream();
} catch( IOException e ) {
throw new UncheckedIOException( e );
}
}
}
Is there any way to get an IProject from this FileStorage?
You can try and get an IFile for a file using:
IPath path = ... absolute path to the file
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IFile file = root.getFileForLocation(path);
if (file != null) {
IProject project = file.getProject();
...
}
But this will only work for a file inside the workspace. For anything outside of the workspace you can't have a project.
In short: No. The FileStorage class is meant to represent IStorage instances for files that are located outside of the workspace. Therefore they are not contained in any workspace project and it is not possible to obtain an IProject for them.
I'm trying to write a unit test that checks if methods were invoked in an order. To do that I'm using Mockito's inOrder.verify() like this:
#Test
public void shouldExecuteAllFileCommandsOnAFileInFIFOOrder() {
// Given
ProcessFileListCommand command = new ProcessFileListCommand();
FileCommand fileCommand1 = mock(FileCommand.class, "fileCommand1");
command.addCommand(fileCommand1);
FileCommand fileCommand2 = mock(FileCommand.class, "fileCommand2");
command.addCommand(fileCommand2);
File file = mock(File.class, "file");
File[] fileArray = new File[] { file };
// When
command.executeOn(fileArray);
// Then
InOrder inOrder = Mockito.inOrder(fileCommand1, fileCommand2);
inOrder.verify(fileCommand1).executeOn(file);
inOrder.verify(fileCommand2).executeOn(file);
}
However, the second verify() fails with the following error:
org.mockito.exceptions.verification.VerificationInOrderFailure:
Verification in order failure
Wanted but not invoked:
fileCommand2.executeOn(file);
-> at (...)
Wanted anywhere AFTER following interaction:
fileCommand1.executeOn(file);
-> at (...)
If I change .executeOn(file) to .executeOn(any(File.class)) the test passes, but I want to make sure that the methods are invoked using the same argument.
Here's the class I'm testing:
public class ProcessFileListCommand implements FileListCommand {
private List<FileCommand> commands = new ArrayList<FileCommand>();
public void addCommand(final FileCommand command) {
this.commands.add(command);
}
#Override
public void executeOn(final File[] files) {
for (File file : files) {
for (FileCommand command : commands) {
file = command.executeOn(file);
}
}
}
}
The test fails because the argument to the second executeOn() method call is not the same file as the argument of the first one, since the first file is replaced by another one in
file = command.executeOn(file);
I want write several tests, but from a high level each of them should populate a directory structure with some files. I'd test each of these cases at least:
A single folder with a file that passes the filter.
A single folder with a file that does NOT pass the filter.
A nested folder with a file in each.
Code:
class FolderScan implements Runnable {
private String path;
private BlockingQueue<File> queue;
private CountDownLatch latch;
private File endOfWorkFile;
private List<Checker> checkers;
FolderScan(String path, BlockingQueue<File> queue, CountDownLatch latch,
File endOfWorkFile) {
this.path = path;
this.queue = queue;
this.latch = latch;
this.endOfWorkFile = endOfWorkFile;
checkers = new ArrayList<Checker>(Arrays.asList(new ExtentionsCheker(),
new ProbeContentTypeCheker(), new CharsetDetector()));
}
public FolderScan() {
}
#Override
public void run() {
findFiles(path);
queue.add(endOfWorkFile);
latch.countDown();
}
private void findFiles(String path) {
boolean checksPassed = true;
File root;
try {
root = new File(path);
File[] list = root.listFiles();
for (File currentFile : list) {
if (currentFile.isDirectory()) {
findFiles(currentFile.getAbsolutePath());
} else {
for (Checker currentChecker : checkers) {
if (!currentChecker.check(currentFile)) {
checksPassed = false;
break;
}
}
if (checksPassed)
queue.put(currentFile);
}
}
} catch (InterruptedException | RuntimeException e) {
System.out.println("Wrong input !!!");
e.printStackTrace();
}
}
}
Questions:
How to create files into each folder?
To prove that queue contains
the File objects that you expect?
The last element in queue is the
'trigger' File?
How to create files into each folder?
Extract the file IO and use a mocked repository for the tests. This means that you will have the IO somewhere else and may wish to use the below to test that.
A temp folder using the JUnit rule With a test folder you create the files to match the test.
To prove that queue contains the File objects that you expect?
.equals works well for File objects I believe.
A single folder with a file that does NOT pass the filter.
I'd pass in the blockers so I can pass in an "Always Pass" and "Always Fail" blocker.
public class TestFolderScan {
#Rule
public TemporaryFolder folder= new TemporaryFolder();
#Test
public void whenASingleFolderWithAFileThatPassesTheFilterThenItExistsInTheQueue() {
File expectedFile = folder.newFile("file.txt");
File endOfWorkFile = new File("EOW");
Queue queue = ...;
FolderScan subject = new FolderScan(folder.getRoot(), queue, new AllwaysPassesBlocker(),...);
subject.run();
expected = new Queue(expectedFile, endOfWorkFile);
assertEquals(queue, expected);
}
}
File files[] = rootDir.listFiles(new FileFilter() {
public boolean accept(File file) {
if (file.isDirectory())
return true;
String name = file.getName().toLowerCase();
if (name.endsWith(".zip") || name.endsWith(".jar")
|| name.endsWith(".z") || name.endsWith(".gz")
|| name.endsWith(".tar") || name.endsWith(".bz2")
|| name.endsWith(".bz"))
return true;
return false;
}
});
As you can see, the code is dirty with "||"
Do you know how to make it better?
With Java 6 or above, this is a perfect case for a FileNameExtensionFilter... except that it extends javax.swing.filechooser.FileFilter instead of implementing java.io.FileFilter.
But it is trivial to write a wrapper for it:
File[] files = rootDir.listFiles(new FileFilter() {
private final FileNameExtensionFilter filter =
new FileNameExtensionFilter("Compressed files",
"zip", "jar", "z", "gz", "tar", "bz2", "bz");
public boolean accept(File file) {
return filter.accept(file);
}
});
Why not use regular expressions?
static final Pattern p = Pattern.compile("\\.(zip|jar|z|gz)$");
and then return p.matcher(name).find();
Some pseudocode solutions:
Iterate over an array
suffixes = [".tar", ".zip", ".jpg"]
for suffix in suffixes:
if name.endsWith(suffix):
return True
Use a set
suffixes = [".tar", ".zip", ".jpg"]
nameSuffix = name.getSuffix()
if nameSuffix in suffixes:
return True
I just finished writing this class:
class FileExtensionFilter implements FileFilter {
private final String[] validExtensions;
public FileExtensionFilter(String... validExtensions) {
this.validExtensions = validExtensions;
}
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
}
String name = pathname.getName().toLowerCase();
for (String ext : validExtensions) {
if (name.endsWith(ext)) {
return true;
}
}
return false;
}
}
usage:
File files[] = directory.listFiles(
new FileExtensionFilter(".zip", ".jar", ".z", ".tar"));
BTW this is a reusable class, you can even wrap it with additional checks using the decorator patter, etc.
PS
just noticed the existence of FileNameExtensionFilter
You could do the following using a statically initialized HashSet. Personally I'd pull the allowed extensions out into some sort of configuration file to make it a bit easier to change, but you don't have to.
n.b. FilenameUtils belongs to Commons I/O which also includes a bunch of classes that make doing this kind of stuff easier. Take a look at FileFilterUtils as well, which simplifies things even further and provides some nice helper methods.
private static Set allowedExtensions = null;
static {
allowedExtensions = new HashSet<String>();
allowedExtensions.add("txt");
allowedExtensions.add("zip");
allowedExtensions.add("jar");
allowedExtensions.add("gz");
}
public void filter() {
File rootDir = new File("/");
File files[] = rootDir.listFiles(new FileFilter() {
public boolean accept(File file) {
if (file.isDirectory()) return true;
String fileName = file.getName().toLowerCase();
String extension = FilenameUtils.getExtension(fileName);
if (StringUtils.isNotEmpty(extension)
&& allowedExtensions.contains(extension)) {
return true;
} else {
return false;
}
}
});
}
You can find the API here:
http://commons.apache.org/io/api-release/
You could statically create a map, and return true if the filename extension is a key in the map.
Or you could try to match the filename against a regular expression (but I'd choose to use a map instead).
Here's my approach. java.lang.Collections is really a nice class! And because we're looking up the given file extension in a HashSet, it's more performant. Although I doubt, that performance really matters in this case ...
// ...
final Set<String> archives = new HashSet<String>();
Collections.addAll(archives, ".zip", ".jar", ".z", ".gz", ".tar",
".bz2", ".bz");
File files[] = rootDir.listFiles(new FileFilter() {
public boolean accept(final File file) {
if (file.isDirectory())
return true;
final String name = file.getName().toLowerCase();
return archives.contains(name
.substring(name.lastIndexOf('.')));
}
});
// ...