0

I have the following class:

public class BookProcessor {

    private Map<String, Double> bookToValueRecord = new HashMap<>();
    private Map<String, Integer> bookToCountRecord = new HashMap<>();
    private int counter = 0;

    public void processBook(Book book) {
        counter++;
        recordBook(book);
        if (counter % 5 == 0) {
            printSomeReport();
        } if (counter % 10 == 0) {
            printAnotherReport();
        }
    }

    private void printAnotherReport() {
       //just print something
    }


    private void printSomeReport() {
        //print something else here
    }

    private void recordBook(Book book) {
        bookToValueRecord.put(book.getTitle(), book.getValue());
        bookToCountRecord.put(book.getTitle(), bookToCountRecord.getOrDefault(book.getProduct(), 0)+book.getCount());
    }
}

My idea was to create a Test class BookProcessorTest with field BookProcessor testee; I dont know if I need to mock it or not? One of my tests will be shouldProcessBook(). In this test I will create a sample Book object, then I will call testee.processBook(myBookObject), but then what should I assert or verify? Should I check if this record has been saved into bookToValueRecord? If yes, how, given that this is a private field? Also, how to test that after saving 5 books into my Map, I am printing some report? Should I test that printSomeReport() has been called? If yes, how, given that printSomeReport is private method?

sammy333
  • 1,384
  • 6
  • 21
  • 39

1 Answers1

2

About how to test your class :

1) You need mocking as the class under test has some dependencies that you need to isolate.
But you don't have any dependencies to mock there.

2) private methods should not be tested and you should not either check that these was invoked during the test.
These are implementation details of the API : the public method processBook().

3) An API that doesn't return anything but create a side effect can be tested only by having a way to notice the side effect. You don't seem having that.

So what ?
In fact you are stuck because a) your class does too much things and b) your class stores things but doesn't provide a way to retrieve these things.

a) The private methods printAnotherReport() and printSomeReport() should in fact be moved into public methods of dependencies that are actually missing. For example introducing a PrintService class would be helpful.
In this way you could mock it and you could so check that these methods are invoked as expected. For example update the class as :

private PrintService printService; // dependency 

BookProcessor (PrintService printService){
   this.printService = printService;
}

b) about the recordBook() private method, if BookProcessor composes some Books (BookProcessor is probably not a convenient name here because of that), BookProcessor should also provide way to retrieve them.
So adding a public method to retrieve added information seems required.
But note that you are not constrained to break the encapsulation of the Maps, you could provide methods that retrieve values from the key passed as parameter :

public Double getValue(String key){
    return bookToValueRecord.get(key); 
}

public Integer getCount(String key){
    return bookToCountRecord.get(key); 
}
davidxxx
  • 125,838
  • 23
  • 214
  • 215