RandomAccessFile and final methods

294 views
Skip to first unread message

brende...@gmail.com

unread,
Jan 26, 2014, 2:27:55 PM1/26/14
to moc...@googlegroups.com
Hi all,

I tried to mock a concrete class with final methods (RandomAccessFile).  This didn't go well as the final methods aren't mocked.  Ouch.  Here's a tiny code sample.

   @Test
   public void testOpen()
           throws Exception
   {
      System.out.println( "open" );
      RandomAccessFile raf = mock( RandomAccessFile.class );
      BlockRandomFile instance = new BlockRandomFile(raf);
      instance.open();  // <-- blows up when the real writeShort() is called
      verify( raf ).writeShort( 0 );
      verify( raf ).writeInt( BlockRandomFile.DEFAULT_BLOCK_SIZE );
   }

This blows up (throws an exception) when the real writeShort() method is called (because the mocked RandomAccessFile doesn't have a real underlying file to write to).

So before I make a large mess to get this to work properly, are there any shortcuts anyone can suggest to mock a class with final methods?  A quick search indicates to me the answer is "no" but I thought I'd ask in case anyone had some ideas.

Thanks for your time!


Patroklos Papapetrou

unread,
Jan 27, 2014, 4:14:35 AM1/27/14
to moc...@googlegroups.com
Hi 
Indeed mockito can't mock final methods or classes. 
But you can use PowerMockito library, which is an integration of PoweMock and Mockito that provides such capabilities. 
Regards

--
You received this message because you are subscribed to the Google Groups "mockito" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mockito+u...@googlegroups.com.
To post to this group, send email to moc...@googlegroups.com.
Visit this group at http://groups.google.com/group/mockito.
For more options, visit https://groups.google.com/groups/opt_out.

Tomek Kaczanowski

unread,
Jan 27, 2014, 4:31:01 AM1/27/14
to moc...@googlegroups.com
hi,

the old rule says "do not mock types you do not own" so I wonder if
what you try to do is really the right thing. Could you please post
some more code of SUT so we could see what you are trying to achieve.

2014-01-27 Patroklos Papapetrou <ppapap...@gmail.com>:
--
Regards / Pozdrawiam
Tomek Kaczanowski
http://practicalunittesting.com

KARR, DAVID

unread,
Jan 27, 2014, 9:57:14 AM1/27/14
to moc...@googlegroups.com
> -----Original Message-----
> From: moc...@googlegroups.com [mailto:moc...@googlegroups.com] On Behalf Of
> Tomek Kaczanowski
> Sent: Monday, January 27, 2014 1:31 AM
> To: moc...@googlegroups.com
> Subject: Re: [mockito] RandomAccessFile and final methods
>
> hi,
>
> the old rule says "do not mock types you do not own" so I wonder if
> what you try to do is really the right thing. Could you please post
> some more code of SUT so we could see what you are trying to achieve.

To the OP, you'll have to user PowerMockito to mock final methods.

brende...@gmail.com

unread,
Jan 29, 2014, 2:18:33 PM1/29/14
to moc...@googlegroups.com

Hi all, thanks for the replies.  I thought I was subscribed to this thread, but I guess I wasn't so I'm just getting back to this question now.  PowerMock sounds interesting, I'll have to look into that.

However "don't mock what you don't own" sounds better here, so I might just add a little wrapper around RandomAccessFile so I can mock it.

As for what I'm trying to do, it's a small wrapper around RandomAccessFile that turns it into a block access with fixed block sizes.  As opposed to a fully random access file that can seek to any byte.

Code here if you care, it's untested and likely has errors


<code>
/*
 * Copyright 2013, 2014 Brenden Towey
 * All rights reserved
 */

package SimpleUtils.files;

import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/** *  A {@code BlockRandomFile} is a random access baseFile that uses
 large, fixed blocks of data as the basis for access.
 *
 * <p></p>
 *
 * @author Brenden Towey
 */
public class BlockRandomFile implements Closeable
{
   private static final int HEADER_SIZE = 128; // bytes
   public static final int DEFAULT_BLOCK_SIZE = 4069; // 4k
  
   protected final RandomAccessFile baseFile;
   protected int blockSize;
  
   protected BlockRandomFile( RandomAccessFile baseFile ) throws IOException
   {
      this.baseFile = baseFile;
   }
  
   public BlockRandomFile( File file ) throws FileNotFoundException, IOException
   {
      file.createNewFile();
      this.baseFile = new RandomAccessFile( file, "rws" );
   }
  
   public void close() throws IOException {
      baseFile.close();
   }


   public void open() throws IOException
   {

      if( baseFile.length() == 0 ) {
         // write header
         baseFile.writeChars( "BlocRand" ); // magic number
         baseFile.writeShort( 1 ); // version
         baseFile.writeShort( 0 ); // version
         baseFile.writeInt( DEFAULT_BLOCK_SIZE );
         blockSize = DEFAULT_BLOCK_SIZE;
         baseFile.setLength( HEADER_SIZE );
      } else {
         baseFile.readLong();  // magic number
         baseFile.readShort(); // version
         baseFile.readShort(); // version
         blockSize = baseFile.readInt();
      }
   }

   public InputStream getBlockStream( long block ) throws IOException {
      InputStream in = new BlockInputStream( block, this );
      closeList.put( in, null );
      return in;
   }

   public byte[] getBlock( long block ) throws IOException {
      byte[] buffer = new byte[blockSize];
      synchronized( this ) {
         baseFile.seek( block * blockSize + HEADER_SIZE );
         baseFile.readFully(buffer);
      }
      return buffer;
   }
  
   // Write a block
  
   public void writeBlock( long block, byte[] buffer ) throws IOException {
      if( buffer.length != blockSize )
         throw new IllegalArgumentException( "Bad buffer size: was " +
                 buffer.length + ", expected " + blockSize );
      synchronized( this ) {
         baseFile.seek( block * blockSize + HEADER_SIZE );
         baseFile.write( buffer, 0, buffer.length );
      }
   }
  
   // Package-private for testing
   class Shutdown implements Runnable {

      @Override
      public void run()
      {
         for( Closeable stream : closeList.keySet() )
            closeWithCatch( stream );
         closeWithCatch( baseFile );
      }

      private void closeWithCatch( Closeable c )
      {
         try {
            c.close();
         } catch( IOException ex ) {
            Logger.getLogger( BlockRandomFile.class.getName() ).
                    log( Level.SEVERE, null, ex );
         }
      }
   }
  
   long newBlock() throws IOException {
      synchronized( this ) {
         long newBlock = ( baseFile.length() - HEADER_SIZE ) /
                 blockSize;
         baseFile.setLength( baseFile.length() + blockSize );
         return newBlock;
      }
   }
  
   public void installCloseOnShutdown() {
      Runtime.getRuntime().addShutdownHook( new Thread( new Shutdown() ) );
   }
  
   protected Map<Closeable,?> closeList = new WeakHashMap<>();
  
}
</code>
 

KARR, DAVID

unread,
Jan 29, 2014, 2:50:56 PM1/29/14
to moc...@googlegroups.com

Concerning the “don’t mock what you don’t own” concept, we’ve discussed this in this list before (you can see my detailed response around 11/4/2013).  Frankly, I’m skeptical.  The types you don’t own are very likely exactly the types you need to mock.  I don’t see the value of adding a facade class simply to say that you’re not mocking a type you don’t own.

 

--

Francisco Olarte

unread,
Jan 30, 2014, 10:27:38 AM1/30/14
to moc...@googlegroups.com
Hi Brenden:

On Wed, Jan 29, 2014 at 8:18 PM, <brende...@gmail.com> wrote:
....
> However "don't mock what you don't own" sounds better here, so I might just
> add a little wrapper around RandomAccessFile so I can mock it.

¿ Which Java platform are you targetting ?

I see this is a recent lib:

> * Copyright 2013, 2014 Brenden Towey

And I've had similar troubles before ( java.io classes being final
and/or difficult to mock, mainly due to being a legacy from a time
where libaries where really badly dessigned ).

If you target current java ( 1.7 ) you maybe better off basing your
class on java.nio classes. 1.7 has SeekableByteChannel, which extends
ByteChannel and is implementd by FileChannel, and seems to be the
interface you would need on a base testable class ( you can split your
class in two, a base one with a SeekableByteChannel in which you put
all the blocking / unblocking / counting methods and a simple derived
one having the logic to open the real files using FileChannel which
has the File opening logic ). This way you can test the one with heavy
logic mocking an interface and the other one using real files if you
want ( you can even reuse your sample class has RandomAccessFile has a
getChannel() method with gives you back a FileChannel which implements
SeekableByteChannel ). I do not know how your BlockInputStream
accesses BlockRandomFile, but I suppose you could put it easily.


Francisco Olarte.

Francisco Olarte

unread,
Jan 30, 2014, 10:33:59 AM1/30/14
to moc...@googlegroups.com
Hi Brenden:

On Wed, Jan 29, 2014 at 8:18 PM, <brende...@gmail.com> wrote:
> However "don't mock what you don't own" sounds better here, so I might just
> add a little wrapper around RandomAccessFile so I can mock it.

David Karr already sent you a message about this. As he said you
normally have to mock INTERFACES that you do not own. If you go for
this route I would suggest writing an interface to be mocked and an
implementing class wrapping a random access file, then rewrite the
class against the interface and mock it for testing. This would also
give you greater flexibility, as you can then do things like wrap a
byte array or a buffer or a collection of blobs in a db in your
interface if the need arises.

Francisco Olarte.

Marcin Grzejszczak

unread,
Jan 30, 2014, 10:52:17 AM1/30/14
to moc...@googlegroups.com
Hi!

Perhaps this is outside the scope of this discussion (or I could misunderstand the purpose of your test) but in your test you are performing verification of implementation of your solution and not whether it's behaviour is as you wish it was. In fact if you change the implementation and the behaviour remains the same your test will fail.

If you want to perform some functional test you can profit (if you are using JUnit) from the for example Temporary Folder Rule create a File through it and pass it through your object under test to test whether it works as it should.
Reply all
Reply to author
Forward
0 new messages