title: how to mock encryption code
body: <p>I am using testng as my testing framework, mockito for mocks.</p>
  
  <p>What I have is: I am trying to write tests for one class that will be tested, the packet processor. The packet processor has two methods called send and receive, where the receive method receives one packet of a specific network protocol from the source input stream, and then parses it's headers, returning actual packet contents. The send method gets the payload, creates a packet, and sends it over the network.</p>
  
  <p>The problem is that those packets are optionally compressed, encrypted and mac is calculated for them. My problem is generally, that encryption, mac calculation and compression is done by separate objects derived from some abstract classes, so I have a need to mock them. However, how can you make the mock of the decryptor work so that it returns meaningful data that can be verified?</p>
  
  <p>I do not seem to be able to just create a dummy packet and return a good one when the decrypt method is called. That is because packets are variable length, and the length is itself encrypted, so what the object under test needs to do is, read first ciphertext block, decrypt, read length from the resulting byte array, then read the rest of the ciphertext of the packet, decrypt, and merge to create the full packet. Then, there will be two decrypt calls per packet if sufficiently long, and I would probably need to not only generate a fake verifiable packet, but also to split it somehow, and do some fancy things. I am not quite sure how to deal with such a situation.</p>
  
  <p>I currently have the written tests and packet sender/receiver for the case where there is no encryption, integrity protection nor compression.</p>
  
  <p>This is the code of the receive method:</p>
  
  <pre><code>public byte[] receive(final DataInput in) throws SshProtocolException, SshIoException {
      assert in != null;
      try {
          int length = 0, paddingLength = 0;
          /* Read packet length, ignoring timeouts and returning null on eof. */
          while (true) {
              try {
                  length = in.readInt();
                  break;
              } catch (SocketTimeoutException e) {} catch (EOFException e) {
                  return null;
              }
          }
          logger.debug("receiving next packet of length {}", length);
          /*
           * Minimum packet length is 16 bytes, the length field does not count itself, so
           * 12 bytes + length field size.
           */
          if (Integer.compareUnsigned(length, 12) &lt; 0) {
              logger.error("malformed packet: packet is too small, must be at least 16 bytes");
              throw new SshProtocolException("malformet packet: packet is too small, must be at least 16 bytes");
          }
          /* Max packet size is 35000 bytes with length field. */
          if (Integer.compareUnsigned(length, 34996) &gt; 0) {
              logger.error("malformed packet: packet too large");
              throw new SshProtocolException("malformed packet: packet too large");
          }
          /* Must be a multiple of cipher block size or 8, whichever is larger. */
          if (Integer.remainderUnsigned(length + 4, 8) != 0) {
              logger.error("malformed packet: packet must be aligned to the cipher block size");
              throw new SshProtocolException("malformed packet: packet must be aligned to the cipher block size");
          }
          /* Read packet. */
          byte[] packet = new byte[length];
          in.readFully(packet);
          paddingLength = packet[0] &amp; 0xFF;
          if (paddingLength &lt; 4) {
              logger.error("malformed packet: padding length must be at least 4 bytes");
              throw new SshProtocolException("malformed packet: padding length must be at least 4 bytes");
          }
          int payloadLength = length - paddingLength - 1;
          if (Integer.compareUnsigned(payloadLength, 32768) &gt; 0) {
              logger.error("malformed packet: payload too large");
              throw new SshProtocolException("malformed packet: payload too large");
          }
          byte[] payload = new byte[payloadLength];
          System.arraycopy(packet, 1, payload, 0, payloadLength);
          logger.debug("padding length: {}, payload length: {}", paddingLength, payloadLength);
          return payload;
      } catch (EOFException e) {
          throw new SshProtocolException("malformed packet received, end of stream before the end of packet");
      } catch (IOException e) {
          throw new SshIoException("i/o error while receiving the ssh packet: " + e.getMessage(), e);
      }
  }
  </code></pre>
  
  <p>This is the example test that tests for success:</p>
  
  <pre><code>@Test
  public void receivingPacketReturnsPayloadWhenPacketValid() throws SshException, IOException {
      byte[] packet = generatePacket(12, 7, "test".getBytes(), 7);
      DataInputStream in = new DataInputStream(new ByteArrayInputStream(packet));
      SshPacketProcessor proc = new SshPacketProcessor(random, inputHolder, outputHolder);
      byte[] payload = proc.receive(in);
      assertEquals(new String(payload), "test");
  }
  </code></pre>
  
  <p>The generatePacket() method generates the test packet using the provided data, and allows generating invalid packets if needed.</p>