PRC2

Faking as a testing technique

"When you can’t or won’t make it, Fake it"

Let’s revisit the movie making metaphor. We know that almost everything in that industry is fake, or maybe more politely make-belief. In that it only needs to look convincing without being the real thing. So for instance, when someone needs to be hit hard in the face, it will not really be done and to make sure nothing untoward happens to the expensive actors, they may be replaced by stunt men and women, so the punch will never land on the pretty face.

In our industry we have other reasons to not use the real thing.

  • It is not available (yet).
  • It is expensive (as in slow) to use and we want our tests to be quick.
  • We want to test behavior under circumstances that are rare, and difficult to achieve with the real thing.
  • It is inconvenient to use.

What we do is replace the "Dependent On Components" by so called mocks.
In all cases, the System Under Test (SUT), the class we are testing is always used as is. So we have no stunt doubles for our protagonist. When testing, we want it to take all the punches, because we want to make sure it can handle them.

Caution:

A very common mistake made when using mocking as a testing technique, is faking the protagonist or SUT. This is very wrong. What you are testing in that case is the mock, not the class you hope to test. None of the code of the SUT will be used in such tests.

fitness
Figure 1. Never imagined coding as a work out.

Suppose you have an application in which one of the required effects is that a business class prints something to a printer. Turning this into a test will give you a hard time, because you have to run between your desk an printer, but hey, it will improve your fitness.[1].

To verify that the system under test (SUT) does indeed use the printer, make that fact observable, so that you can verify that the printer call has been made. You can use a trick common to many informatics problems and is available to you too: add a level of indirection, or do not give the actual printer to the business class but instead something that the business code thinks it is a printer and replace that with whatever suits you for the test.

annoyed 32 'Nice' you say, now I have two problems':

  1. change the application and
  2. create a printer just for my tests.

Stay cool, have a Mockito.

mockito drink
Figure 2. it gets better.

You are not the first developer in this situation, so someone has automated the creation of 'fake' objects or mocks for us:
Mockito is a framework that can create various objects that are useful for testing.

Mock is an object that behave like a (alternate) implementation of a class or interface. This is akin to an actor (or actress) or stunt-double that behaves exactly like you tell it to. A mock also saves any method call that you do on it.
Stub just holds data that you provide and are presented to the SUT.
Both mock and stub are stand-ins for dependent on components (DOC) or collaborators for the SUT.
There is also a Spy which is in fact a wrapper around an actual implementation, that allows you to observe what goes in (method call+ parameters) and comes out of (return values) of the real object while it is used by the SUT. You can make the spy only spy on certain method calls, and leave the rest unchanged.

mockstubsspies
Figure 3. Class diagram explaining mocks, stubs and spies

Lets see what this looks like in code.

The printer interface.
public interface Printer {

    void println( String print );

    /**
     * Make it deal with objects too.
     * @param o to print.
     */
    default void println( Object o ) {
        printLn( o.toString() );
    }
}

Now over to the test class, which creates a mocked printer and hands it to the Business class.

Business test class.
    @Test
    public void doesItPrint() {
        Printer pr = mock( Printer.class );  1

        Business b = new Business( pr );     2

        b.work();                                 3

        verify( pr ).println( anyString() ); 4

    }
  1. Create the mocked printer.
  2. Create a Business object passing it the printer.
  3. Make the business class work.
  4. Ask the mocked printer if it was used.

This is of course a simple test and the only thing that it verifies is that the printer.printLn(String) has been used.

Verify that what is printed is okay.
@Mock
Printer printer; 1

Business business;

@BeforeEach
void setup() {
    business = new Business( printer );  2
}

@Test
void doesItPrintBusiness() {
    var lineCaptor = ArgumentCaptor.forClass( String.class ); 3

    business.work( "Linda" ); 4

    verify( printer ).println( lineCaptor.capture() ); 5
    assertThat( lineCaptor.getAllValues() ).contains( "Hello Linda" ); 6
    //  fail("test does It print ended. You know what to do.");
}
  1. Setup mock as annotated field.
  2. Create the business (SUT) object in a setup method, so tests can avoid repeating this,
    again passing the printer object via the business constructor.
  3. Prepare a Mockito helper object to capture the data passed to the printer.
  4. Make business do its thing.
  5. Verify that printer.println(…​) is called. The captor object is used to collect all that has been received by the printer’s printLn method.
  6. The expected data is passed to the printer, so a real printer would print it. The method lineCaptor.getAllValues() produces a List of the captured things, strings in this case.

Mockito is well documented with lots of examples in its java doc. When you look to the test dependencies of your Maven project in NetBeans-IDE, right-click on the mockito dependency, then download the javadoc, you can enjoy that while you are developing.

To Mock or To Configure

Sometimes the class that you want to pass as a mock to the business code has a big interface, with many methods, and you do not want to restrict the business code to just a few trained methods. That would constrain the methods that the business class could use, which serves no real purpose. In such cases it may be better to provide a differently configured real class.

Input stubbing with a real class.

An object that contains pre-configured data is called a Stub.
As an example: Assume your business class uses a Scanner as it’s input. Scanner has 50-odd methods, which you certainly do not want to train all when mocking.

But since you will pass a special Scanner to the business class anyway, the better way is to use a Scanner configured with one of it’s constructors. In this case use new Scanner( String s ), in which the string s contains exactly the input you want to use in your test. The string can contain newlines, so that it appears as if the user entered multiple lines. This allows you to test a program with a dialog. Something along the line of new Scanner("12\n13\n");, that is '12', 'newline', and '13'.

Output mocking with a real classes

THIS PARAGRAPH DESCRIBES AN ADVANCED TOPIC THAT YOU DON’T NEED IMMEDIATELY. FINE IF YOU UNDERSTAND IT, IF NOT THEN JUST CONTINUE WITH THE NEXT PARAGRAPH ABOUT TESTABLE DESIGN.

For the output side something similar is in effect. The most common way of outputting something is to use System.out to print or otherwise output the results of the program. But System.out is just a PrintStream, which also has a lot of convenience methods with a total of 33 methods and 10 constructors. You do not want to restrict the use of any of its methods in the business class.

The trick here is to use one of the constructors to given PrintStream something to 'print on', which you can inspect afterwards. Usage: The business class has a constructor.[2] or setter to take a PrintStream and always uses that to print to, if it needs to print anyway.

Usage is business code
class PrintingBusiness
      final PrintStream out; 1

      PrintingBusiness() { 2
          this( System.out );
      }

      PrintingBusiness( PrintStream ps ) { 3
          this.out=ps;
      }

      void businessMethod(){
          out.format( "%s at %d", "Oplegkaas", 957 ); 4
      }
}
  1. final to have a constructor set it
  2. Default constructor uses System.out to print to and forwards it to the next constructor.
  3. Redirect output to other output, for test or 'printing' in a GUI.
  4. All facilities of PrintStream are available, including formatted printing.
Use in tests
    @Test
    public void tformattedPrint() {
        ConsoleOutput cout = new ConsoleOutput();
        PrintStream out = cout.asPrintStream();

        out.format( "%s at %d", "Oplegkaas", 957 );

        assertThat( cout.toString() ).contains( "at", "Oplegkaas", "957" );
    }

By implementing the AppendAndClear interface you can print to anything, for instance by using a StringBuilder as intermediate carrier and then transfer the output to a GUI element in JavaFX or Swing.

Definition of AppendAndClear interface.
@FunctionalInterface
public interface AppendAndClear {

    /**
     * Append a string to this AppendAndClear.
     *
     * @param toAppend text to add.
     */
    void appendText( String toAppend );

    /**
     * Clear the output. Optional operation.
     */
    default void clear() {
    }
}
Implementation of ConsoleOutput
package consoleoutput;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * Test helper, to be able to redirect a PrintStream to any writable.
 *
 * Use case: write to string or StringBuilder for test or e.g. javafx
 * TextInputControl for UI. Implement AppendAndClear (which can be done as a
 * lambda) and you're set.
 *
 * @author Pieter van den Hombergh {@code <p.vandenhombergh@fontys.nl>}
 */
public class ConsoleOutput extends OutputStream {

    protected final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    final AppendAndClear aac;

    /**
     * Create a Console output copying data to given AppendAndClear.
     *
     * @param ta append and clear to copy all written text to.
     */
    public ConsoleOutput( AppendAndClear ta ) {
        this.aac = ta;
    }

    /**
     * Without appendable.
     */
    public ConsoleOutput() {
        this( null );
    }

    @Override
    public void write( int b ) throws IOException {
        if ( aac != null ) {
            String s = "" + ( (char) b );
            aac.appendText( s );
        }
        baos.write( (byte) b );
    }

    /**
     * Clear and discard output.
     */
    public void clear() {
        if ( aac != null ) {
            aac.clear();
        }
        baos.reset();
    }

    /**
     * Get the accumulated string since start or clear.
     *
     * @return the string
     */
    @Override
    public String toString() {
        return new String( baos.toByteArray(), StandardCharsets.UTF_8 );
    }

    /**
     * Factory method to get this ConsoleOutput as PrintStream.
     *
     * @return the print stream.
     */
    public PrintStream asPrintStream() {
        return new PrintStream( this, true, Charset.defaultCharset() );
    }
} // eof ConsoleOutput

Design for Test-ability

Also known as dependency injection made simple.

A very important non functional requirement of good software is Test-ability.
Test-ability can be achieved or improved by decoupling the class you are developing (the SUT) from it’s environment, such that you have full control over that environment in your test and can exactly control and observe what goes in or out of the SUT.

One first easy setup is to use package private visibility more often. It is actually the default in java, and with reason. When you then put your unit test in the same package (but it its own source tree), you can have the test class look at or use the package private members of the class in its assertion or verification very easily. Most of unit testing is white box testing anyway, which almost always includes inspecting private members.

Test-ability is also improved by using the design rule program against interfaces, not concrete classes.
If you do that in your design, you can very easily make specialized implementations of those interface methods that you need in your test, to provide those specific values or reactions that you need in your test, without having to jump through a lot of hoops to get the test set up.
This easy setup is where Mockito comes in.

It is not the business' business to worry about:
persistence
where service come from or who implements them.

By injecting or otherwise providing the business with the services it needs makes the business code testable without making it dependent or even know the actual implementing classes. And that is really all that counts. The business code makes the application valuable, the rest is either plumbing or already available code.[3].

Warning:

Never let the business code create its own persistence or or service object. You would loose control over what flows between service and business class, and makes it unobservable, which is bad from the testing standpoint.

Instead hand the service or resource to the business, or give it some provider of a resource.

Business class fit for testing

WE’LL FURTHER EXPLAIN THIS CONCEPT IN CLASS AND IN THE CONTEXT OF PRJ2. THERE ARE QUITE SOME DESIGN IDEAS BEHIND IT, SO DON’T WORRY IF YOU DON’T UNDERSTAND IT IMMEDIATELY.

Let us look at an example as a class diagram.

businessarch
Figure 4. Giving the business what it really needs make the business testable
businesstest
Figure 5. business class in the test lab

In the picture above you see a class diagram which shows a detail in which a business class 'lives' in an environment that is designed to make sure the business class sticks to its business:

Dealing with business entities such as customers and products.
Uses services to do the business' bidding, such as saving stuff to a database, provided as a service to the business class.
And the business code NOT responsible for how to show things in a UI.

In the above example we provide a factory or provider of the services that the business code requires.

The business class appears in two worlds:

  1. A business world, the Application. This is the actual use and role in the whole application. In that world there is a so called Assembler, that stitches the parts of the application together and sets it on its way. Typically this Assembler role is what the main class in a project does. Find the parts, put them together, and put them to work.
  2. In the Test world, call it the laboratory or business.[4] school, where the business class is trained and tested. Here the test class does the assembling with a dash of Mockito to get good grips on what goes into and out of the business class.

Before the business class can used in real life, it has to be trained and tested. Feels a bit like a student right?

You should note the arrows in the diagram. They provide direction and make it the diagram a directed graph, and lets a dependent point to a depends-on, like the BusinessClass depends on a Service, which is an abstract class or better still, an interface. Also note that the service does not point to, or is dependent on the business, which is good, because it makes the service reusable in other contexts.
The service implementing classes are dependent on the Service definition which is shown with the "implements arrow", pointing from sub to super type.

In the business school, there is a test class, that creates instances of the business class and hands it implementations of the services the business needs, but does that by giving it a specialized service factory that produces mock service implementations. The business class is NOT aware of that. It uses the service in the way it is programmed, which allows the test class to verify that that programming is correct. The test class is in complete control and can also give the business class situations that are extensions to the normal happy path to ensure that those situations are also handled properly. If that sounds like an exam setting at school, you would be right.

You can tell these worlds in the class diagrams apart by the fact that one has a pink BusinessTest class with test methods.

In the 'normal' non-testing Application world, the design has taken some provisions to make sure the business class can work in both worlds. To provide the business class with a factory from which it can obtain Service implementations, a so called Assembler is added. This Assembler, in the figure at the top of the application class diagram, typically creates or instantiates the business class and provides it with all it needs, in this case an implementation of a ServiceFactory. Note that the assembler in this case also creates the UI part. In desktop applications this is the responsibility of the Main class of the application.

This approach of providing an instance of some class implementing an interface or realizing an abstract class to a business class that needs it is called dependency injection.

Dependency injection is providing the services required by some class by means of constructor parameters, setters or otherwise, to avoid that the dependencies of the class are created by that class itself.


  1. (source picture "https://www.menshealth.com/nl/fitness/")
  2. best
  3. And hopefully tested
  4. pun intended