Test::Unit::Mock: Mock objects for testing with Test::Unit 
Author Message
 Test::Unit::Mock: Mock objects for testing with Test::Unit

Hi fellow Rubyists,

I'd like to announce the initial release of Test::Unit::Mock, a mock
object class for Test::Unit test suites.

Test-Unit-Mock is a class for conveniently building mock objects in
Test::Unit test cases. It is based on ideas in Ruby/Mock by Nat Pryce,
but is a bit (IMHO) easier to use, and more flexible.

It allows you do make a mocked object that will respond to all the
methods of the real class (albeit probably not with correct results)
with one line of code. Eg.,

  mockSocket = Test::Unit::MockObject( TCPSocket ).new

You can then specify return values for the methods you wish to test in
one of several different ways:

  # Make the #addr method return three cycling values (which will be
  # repeated when it reaches the end
  mockSocket.setReturnValues( :addr => [
      ["AF_INET", 23, "localhost", "127.0.0.1"],
      ["AF_INET", 80, "slashdot.org", "66.35.250.150"],
      ["AF_INET", 2401, "helium.ruby-lang.org", "210.251.121.214"],
    ] )

  # Make the #write and #read methods call a Proc and a Method,
  # respectively
  mockSocket.setReturnValues( :write => Proc::new {|str| str.length},
                              :read => method(:fakeRead) )

  # Set up the #getsockopt method to return a value based on the
  # arguments given:
  mockSocket.setReturnValues( :getsockopt => {
      [Socket::SOL_TCP,    Socket::TCP_NODELAY] => [0].pack("i_"),
      [Socket::SOL_SOCKET, Socket::SO_REUSEADDR] => [1].pack("i_"),
    } )

You can also set the order in which you expect methods to be called, but
you don't have to do so if you don't care:

  mockSocket.setCallOrder( :addr, :getsockopt, :write, :read, :write )

By default, when testing for call order, other method calls may be
interspersed between the calls specified without effect, and only a
missing or misordered method call causes the assertions to fail. If you
want the call order to be adhered to strictly, you can set that:

  mockSocket.strictCallOrder = true

Then, when you're ready to test, just activate the object and send it
off to whatever code you're testing:

  mockSocket.activate
  testedObject.setSocket( mockSocket )
  ...

  # Check method call order on the mocked socket (adds assertions)
  mockSocket.verify

Assertion failures contain a message that specifies exactly what went
wrong, eg.:

  $ ruby misc/readmecode.rb

    1) Failure!!!
  test_incorrectorder(MockTestExperiment) [./mock.rb:255]:
  Call order assertion failed: Expected call to :write, but got
    call to :read from misc/readmecode.rb:77:in `test_incorrectorder'
    at 0.00045 instead

    2) Failure!!!
  test_missingcall(MockTestExperiment) [./mock.rb:255]:
  Call order assertion failed: Missing call to :read.

If you require more advanced functionality, you can also use the mocked
object class as a superclass:

   # Create a mock socket class
   class MockSocket < Test::Unit::MockObject( TCPSocket )
        def initialize
            super
            setCallOrder( :read, :read, :read, :write, :read )
            strictCallOrder = true

        end

       def read( len )
           super # Call the mocked method to record the call


           return rval
       end

       def write( str )
           super # Call the mocked method to record the call

           return str.length
       end
   end

You can also add debugging to your tests to give you a timestamped
history of each call to the mock object:

  # Call the methods in the correct order
  mockSocket.addr
  mockSocket.getsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY )
  mockSocket.write( "foo" )
  mockSocket.read( 1024 )
  mockSocket.write( "bar" )
  mockSocket.read( 4096 )

  # Check method call order on the mocked socket
  mockSocket.verify

  if $DEBUG
    puts "Call trace:\n\t" + mockSocket.callTrace.join("\n\t")
  end

This outputs something like:

  Call trace:
    addr(  ) at 0.00015 seconds from readmecode.rb:64:in `test'
    getsockopt( 6,1 ) at 0.00030 seconds from readmecode.rb:65:in `test'
    write( "foo" ) at 0.00040 seconds from readmecode.rb:66:in `test'
    read( 1024 ) at 0.00050 seconds from readmecode.rb:67:in `test'
    write( "bar" ) at 0.00063 seconds from readmecode.rb:68:in `test'
    read( 4096 ) at 0.00072 seconds from readmecode.rb:69:in `test'

You can read the documentation or download it from the project page at:

  http://www.*-*-*.com/

I hope it's useful. Thanks to Nathaniel Talbott for all his work on
Test::Unit, and to Nat Pryce for his on Ruby/Mock.

--

Rubymage, Believer, Architect
The FaerieMUD Consortium < http://www.*-*-*.com/ ;



Sun, 21 Aug 2005 09:13:11 GMT  
 Test::Unit::Mock: Mock objects for testing with Test::Unit

Quote:

> Hi fellow Rubyists,

> I'd like to announce the initial release of Test::Unit::Mock, a mock
> object class for Test::Unit test suites.

> Test-Unit-Mock is a class for conveniently building mock objects in
> Test::Unit test cases. It is based on ideas in Ruby/Mock by Nat Pryce,
> but is a bit (IMHO) easier to use, and more flexible.

Excellent!  I was actually thinking about such changes to Mock object
myself.  I'm glad to find out someone else has already done the work.
:-)

--

---------------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)



Sun, 21 Aug 2005 20:27:51 GMT  
 
 [ 2 post ] 

 Relevant Pages 

1. Unit testing - Using Java Mock Objects in Python?

2. Unit testing or mocking Net::SMTP

3. ruby/mock and test::unit

4. using test::unit for C++ unit tests

5. Test-first and mock objects with PyOpenGL

6. Test order in Test::Unit

7. Test order in Test::Unit

8. Automating UI tests (was Re: Art of Unit Testing)

9. Unit testing data; What to test

10. Test Tool for Unit Tests?

11. ANN: Test::Mock 1.0

12. test test test test

 

 
Powered by phpBB® Forum Software