3 Replies Latest reply on Nov 16, 2007 9:18 AM by timfox

    Testing the wireformat

    timfox

      Now that testing is on my mind, here's a suggestion for testing the new JBM remoting code:

      MINA is a third party component, so the unit tests shouldn't really test with the real MINA.

      Instead, abstract out a simple interface that represents the interface between our new remoting code and MINA.

      Our new remoting code then deals with that interface, not MINA directly.

      Create a simple mock implementation of the interface which just forwards requests responses directly from client->server or vice versa (no actual sockets required).

      Create your unit tests using that mock implementation.

        • 1. Re: Testing the wireformat
          jmesnil

           

          "timfox" wrote:
          Now that testing is on my mind, here's a suggestion for testing the new JBM remoting code:

          MINA is a third party component, so the unit tests shouldn't really test with the real MINA.

          Instead, abstract out a simple interface that represents the interface between our new remoting code and MINA.

          Our new remoting code then deals with that interface, not MINA directly.

          Create a simple mock implementation of the interface which just forwards requests responses directly from client->server or vice versa (no actual sockets required).


          The unit tests I put in the BRANCH_JBMessaging-544 (refactored but not committed yet) no longer require sockets.
          They use MINA's own ProtocolCodecSession which encode & decode the packets using the same codec than when they're sent on the wire but there is no network trip.

          All the wireformat tests looks like that:

          1/ create the packet
          2/ encode & decode it using MINA protocolCodecSession
          3/ retrieve the decoded packet
          4/ check it is equivalent to the created packet

          the code for (2) is straightforward:

           private AbstractPacket encodeAndDecode(AbstractPacket packet)
           throws Exception
           {
           IoBuffer buffer = encode(packet);
           return decode(buffer);
           }
          
           private IoBuffer encode(AbstractPacket packet) throws Exception
           {
           ProtocolCodecSession session = new ProtocolCodecSession();
           ProtocolEncoder encoder = new PacketCodecFactory().getEncoder();
           encoder.encode(session, packet, session
           .getEncoderOutput());
           IoBuffer buffer = session.getEncoderOutputQueue().poll();
           return buffer;
           }
          
           private AbstractPacket decode(IoBuffer buffer) throws Exception
           {
           ProtocolCodecSession session = new ProtocolCodecSession();
           ProtocolDecoder decoder = new PacketCodecFactory().getDecoder();
           decoder.decode(session, buffer, session.getDecoderOutput());
          
           Object o = session.getDecoderOutputQueue().poll();
          
           assertTrue(o instanceof AbstractPacket);
          
           return (AbstractPacket) o;
           }
          



          • 2. Re: Testing the wireformat
            timfox

            Currently our "unit tests" in JBM are actually a mixture of pure unit tests and integration tests.

            Pure unit tests should not have any dependencies on other components - so that means they shouldn't use MINA, database, JGroups etc.

            For the JBM 2.0 timescale one of my goals is to clean up the separation between unit tests and integration tests.

            This means each component should not deal directly with the third party component interface, but we should abstract out our own interface(s).

            We then create a mock implementation of that interface(s) which is used for unit testing.

            E.g. for our clustering tests, we currently use JGroups directly. Instead we should abstract out our own group management interface(s) which suits the need of JBM (actually we kind of have this already), then we create a mock implementation - which for unit tests trivially just forwards messages directly in vm. We would then have another implementation for the real world which actually uses JGroups.

            Similarly for the wire format - instead of calling the MINA interfaces directly - we should abstract out that subset of functionality we need in a set of interface(s) and create a mock implemention that just forwards requests/responses directly invm. The unit tests should then use the mock implementation.

            It's not going be overnight that we refactor all our tests to work this way, but for the new work we should certainly aim to do it in this way.

            One benefit of doing this (certainly for clustering anyway) is our test suite will be lightning fast!

            And, as Adrian has pointed out many times probably ;) , we can create interesting error conditions that would be hard to do with the real product.

            Testing failover also becomes very fast and thorough with mocks, we can cause failover to occur at any point in message passing, without having to start lots of servers up (which can take ages) and it all can run in vm.

            Once we get over the initial mindset of doing it this way, I think we'll really see the benefits :)

            • 3. Re: Testing the wireformat
              timfox

              I should probably clarify myself here and draw a distinction between "mock objects" and "fake objects".

              Mock object = typically very lighweight throwaway object whose implementation may be written for a single test. Typically it does stuff like assert correct values are passed in etc.

              Fake object = A fake implementation of the interface or interfaces that typically can be re-used by many different tests.

              E.g. we could create a "fake group management layer" which takes the place of JGroups and allows us to test distributed destinations quickly and in vm.

              Or a fake database which just stores data in a map in memory - enough to run tests against.

              Or a fake MINA which just forwards request/responses from client to server calling the correct objects at the correct times.