1. Reading

  • Horstmann Core Java, Ed 11, Vol II, Ch6

2. Design for Testability

testable business
Figure 1. Giving the business what it really needs make the business testable

It is not the business business to worry about

  • persistence

  • where service come from or how they are implemented

By injecting or otherwise providing the business the services it needs makes the business code testable. And that is really all that counts. The business code makes the application valuable, the rest is either plumbing or already available code.[1].

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

  • dealing with business entities such ad customers and products.

  • uses services to do the business, such as saving stuff to a database.

  • is NOT involved in showing things in a UI.

The business class appears in two worlds . A business world, the application. . In the test world, name it the laboratory or business school, where the business class is trained and tested. Before the business class is used in real life, it has to be trained and tested. Feels a bit like a student no?

You should note the arrows in the diagram. They have a direction and let a dependent point to a depends-on, like the BusinessClass depends on a Service, which is an abstract class or better, an interface. And the Service itself is depended on by the implementing classes that implement the services for a specific role. In the example you could think of the service being a DAO and the ServiceImpl a specific DAO, like DAO<Product>.

In the business school, there is a test class, that creates instance of the business class and passes it implementations of the services it requires, 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 is trained, which allows the test class to verify that that usage 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.

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

In the 'normal' non-testing 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 with which it can get Service implementations, a so called Assembler is added. This assembler typically creates the business class and provides it with all it needs, in this case an implementation of a ServiceFactory. Note that the assembler also creates the UI part, and in desktop applications this is what the main class does. In a Java web container which we will use for our rest services, the web container provides this assembler service to your application code.

This approach of providing a class is called dependency injection.

3. When you can’t or won’t make it, Fake it.

This is not fake news.

fitness
Figure 2. 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. Running 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.[2].

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 all informatics and now to you too: add a level of indirection, or do not give the actual printer to the business class but instead something that it thinks it is a printer and replace that with whatever suits you for the test.

Nice you say, now I have two problems:

  1. change the application and

  2. create a printer just for my tests.

3.1. Stay cool, have a mockito.

mockito drink
Figure 3. it gets better.

You are not the first developer in this situation, so someone has automated the creation of 'fake' objects or mocks.

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 4. 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() {
        ArgumentCaptor<String> 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 field
2 and business 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 it’s 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 And 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 open the test dependencies of your project, right-click on the mockito dependency, then download the javadoc, you can enjoy that while you are developing. During a performance assessment the Javadoc should have been preloaded so is available in the same way.


4. Exercises week 6

Exercise 6.1: My First Mocks and Spies

Extend the example in the theory above For this exercise, have a look at the BiggerNumber Exercise from PRC1, Week 1. It was a small exercise in which the user inputs two numbers, and the program returns the bigger of that number. You should write such a program again, but this time use Mockito to test it.

In your business code, have a scanner that reads from some input, such as System.in. In your test code, create a Scanner that instead takes a string and reads from that.

Giving a different scanner to the business code.
  Scanner scanner = new Scanner("Here be some business relevant string")
  Business = new Business( printer, scanner);

If you want to expand on the small program, have it append to some abstract type, like an Appendable. From the api you can see that it is implemented by many classes, meaning in a real application these could all be used as target. You could also have it use some interfaces or (abstract) types and verify that the business collaborates with those types in the required way.

Mocking a final class is not enabled by default, and is only available in Mockito versions above 2.0.

By default mockito simply extends the mocked class (or implements the interface), and with a final class you can’t extend. Nor can you overwrite a final method in any class.

But enabling an Mockito extension makes mocking finals possible anyway. To enable said Mockito extension you need to do the following in the root of you project:

  • Create a directory src/test/resources/mockito-extensions

  • Create a file in that directory named org.mockito.plugins.MockMaker after the extension and put the line mock-maker-inline in that file.

For the command line aficinados, too copy and paste in the terminal:

mkdir -p src/test/resources/mockito-extensions
echo 'mock-maker-inline' > src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
Exercise 6.2: Perishable Products

Selling Perishable Products

Build part of a cash register system used in sales of products that might be perishable.

Class diagram

perishablesales

In the design we left out the GUI-controller, which would normally invoke the correctSalesPrice(bb: LocalDate), the submit(), and the print() methods on the cash register business component.

Perishable products are products with a limited shelf life. Think of fresh products such as milk, meat, vegetables, fruit, or anything that has a best before date.

Assume that all products are labeled with a bar-code which identifies the product and that a perishable product has a best before data or similar.

The cashier normally scans the product and that product is marked sold and optionally appears on the cash receipt.

A perishable product undergoes the same procedure, but in case the best before date is less than 2 days, the product should also be marked sold, but the the sales price should be 35% off. 0 days left 65% off, less than or equal 0 days left 100% off, no longer on sale but product will be donated to a food bank.

The cash register uses the following service API

Sales API
   Product lookupProduct(int barcode);
   int salesPrice(int barcode, LocalDate bestBefore);
   String sold(SalesRecord sale);

The responsibility of most of the methods of most types should be obvious. As extra info: the SalesService.sold produces a string which is supposed to appear on the cash receipt and should therefor be printed using the Printer interface. Printing of all data is postponed until the cashier can ask: Do you want a receipt? The sold items should be ordered on the receipt as follows:

  • Perishable products should come first, non-perishables second.

  • The sold items should be printed in scan order.

    • If an item has been scanned multiple times, it should not be added to the list, but instead the count for the item should increase.

      • If a second or third perishable product is scanned with a different best before date that affects the sales price it should be added as new item.

The CashRegister is given a clock and a SalesService at construction time. In dependency injection parlance this is called constructor injection for obvious reasons.
The clock is to be used to determine the actual date, the sales service to register to look up products and perform the sales registration. Both SalesService and Printer should be mocked for the purpose of testing.

Actual payment has been left out of this exercise.

Abbreviated Use Case Normal sale

As a cashier I want to scan a product, compute a sales record and submit the sales record when I scan the next product. This can keep me in a continuous flow of scanning products. Beep, Beep, Beep…​.

Abbreviated Use Case sale of Perishable Product

As a cashier I want to scan a perishable product and select the best before date that I can read on the label. When the product is perishable, I expect a calendar to be shown, in which I can select the best before date of the actual product. I have two alternatives:

  1. When I select a best before date on the presented calendar, the cash register should correct the price if applicable.

  2. When I select submit, the best before date is not considered.

  3. When I did not select a bb date before I scan the next product, then the register should beep with a low tone and wait for me to comply.

After either alternative the cash register should construct a sales record and submit it to the Sales Service. Then the cash register is ready for the next scan, or complete.

Abbriviated Use Case Print receipt

As a cashier I will ask the customer if he or she wants a receipt. If the answer is yes, I select print and the printer should print a receipt as specified above.

Your Tasks
Design your test. You are supposed to use Mockito to mock the SalesService and Printer. Complete the class diagram with the things needed for testing and submit too. The updated class diagram is part of the exercise.

The product and SalesRecord classes are given, complete with tests. There are no implementations for SalesServiceImpl and Printer yet, nor should they be created. Mock these services.

The mocked printer should be used to test that the information is printed in the specified order. In the implementation of the your CashRegister you can use a structure like a map of maps to organize the inputs. Choosing the proper map implementation is the trick here. Get inspired by the lava.util.Map API.

Test driven develop the Cash Register class.

It is sufficient to throw an exception when the perishable’s best before date is not selected. Beeping in a low tone can be done in a later version.

Hints in designing your tests:

  • First sell one product, to verify that printing contains all the relevant info for the product, quantity and price.

    • printReceipt and ask the mock what it received and inspect (verify, assert) that.

  • Then sell two of the same products (two scans, same barcode), non-perishable, to see if the count and price are correct. Same assertions, different values.

  • Then do the same with some perishable product, with one of the items best before date near expiry. (For say 35%).

  • Then scan more products and ensure that the cash receipt contains the information as per requirement.

  • same approach with testing if the sales service is used properly. Here you do not need to keep the order.


4.1. Test run Reports Week 6


1. And hopefully tested
2. (source picture "https://www.menshealth.com/nl/fitness/")