1. Reading

2. Test quality

So testing is to improve code quality. But is there any quality measure to tests?

Sure there is:

  • Work test driven, duh. In the standard TDD cycle, you should have your test to be red first. That’s one.

  • You can break your code and see if the tests make you aware of it. They should show a nice deep red (at least some of them). If they do not, you are working according to the TDD cycle. That’s two.

  • You can measure the coverage of your tests. Sebipom provides a tool for that called JaCoCo that you can turn on in your IDE or when running form the command line. It shows how much of the business code is executed by your tests. That’s three.

  • For the lazy ones (aren’t we all) and easily repeatable too: Have you code automatically mutated by some tool. Your tests should find most if not all issues after mutating. The tool provided for this is pit-test[1]

Let’s go through the method one by one.

2.1. TDD

These you know:

  • There is no test before there is a requirement.

  • And there is no business code before a test.

So if a method is not specified, then do not write a test for it, and do not write the method either.

The only reason to have a method is because the specs demand it. And they should state

  • what the method is supposed to do

  • in which states of the object the method call is legal

  • any requirement of the parameters to the method

  • specification of result, possibly in a set of input-output tests cases.

  • any exceptions that can be thrown and under what conditions

  • the state that the object is left after completion of (or return from) the method.

If the user stories are complemented with proper scenarios (which they should to help you write your test), then you can write the tests easily, feature for feature and thereby test-driven develop the business code.

Writing your test first, which makes the business break first.
Always give the IDE a chance to code-complete whatever you try to invoke in your test, because the business code does not yet exist, so that the test is meaningful. A IDE generated throw new UnsupportedOpretaionException( "this method needs implementation" ) is not very useful, but returning something of the proper type (for non-void methods) is a better choice. If you think it is a sly trick to return the correct value the first time, then you are in bad luck because:

  1. You will have a test that has not been red.

  2. You will have to invent other test cases, to complement the 'correct' one.

So best start with return 0, false or null. If your test is already green in that case, choose another 'impossible' value such as 42, true or a simplest version of the expected result type , such as "wrong" for Strings.

2.2. Break your code

mario smashes
Figure 1. take that!, from imgflip.com meme generator

If you do not, we will force you. We will regularly, but only after a commit, ask you to break some of the business code to see that the tests notice. We will also do something similar in the correction of the performance assessment, that is we will feed your test broken business code and the test code should notice that.

As promised, breaking your code by a program is easy. It is built in by the magic of pit-test.

Command line First
mvn test org.pitest:pitest-maven:mutationCoverage
maven pittest
Figure 2. Pit mutationCoverage is about in the middle.

You can also use NetBeans ID. Click on the project. Then the navigator should show the possible maven goals.
Choose pittest mutationCoverage. This will produce an index.html report in target/pit-reports which contains date-time-stamped folders cont. Choose the latest if you have more of those folders.
You can also have NetBeans IDE remember this by going run maven → Goals →, then in the dialog add test org.pitest:pitest-maven:mutationCoverage and save it as e.g. pit.

This last results in an addition to the nbaction.xml file like this:

Pit saved in nbaction.xml

3. Slides

4. Lesson Demos

5. Exercises week 5

Exercise 1: Simple Stream

Stream starter exercises

In this exercise you work with the simplestream project.

Starting with streams can be a bit confusing. You will have the best experience if you let NetBeans+ the compiler do some of the thinking for you. And then throw in some tricks.

You can consider a pipeline to have two ends: the start, which is the method that typically is called stream() and the end, which is your terminal operation. Often you can start coding by adding the terminal operation from the beginning and insert any intermediate operation between the start and this end of the stream.

count all students.
  int studentcount = students.stream()

There will be some example code in the simplestream project in your repository.

We have the following facts on the students.

There are 50 students in total. The number of male students is 16. The youngest student is s Shirleen Simpson, student number 3134539. The female students whose first name starts with an 'A' are

Since you are experimenting with streams, do not bother to write business methods. There are four test methods in total, which you need to implement. Inside such method, writhe stream code and the assertJ assertions. The javadoc above the tests give some extra hints.

Write tests for operations to compute from the given data set:

  • The total number of students.

  • The number of male students.

  • The youngest student with. The stream method to use returns Optional<Student>. Think of the proper terminal operation.

  • The list of femail students whose name start with 'A'. Use Collectors.toList() to collect the matching students.
    You can either combine two predicates with the proper logical method or concatenate the strings, before you do the test.

  • The list of student than do not have an a or A in their name. Try with concatenating first and last name and with an or in the predicate, using separate method names.

  • The list of students that match a certain criterium, expressed as a Predicate<Student>.
    Construct the predicate and invoke the method with this predicate as parameter. Be imaginative with the functionality.

example predicate
    Predicate<Student> redHaired s -> s.getHairColor() == HairColor.RED;

In all cases use streams and lambdas.

(1) Add a field to the Student class holding of Map of study-topics to grade: Map<String,Double>.
(2) Stream the students and flatMap the students to a stream of grades and compute the overall average of the grades.
(3) Stream the students and flatMap to Map.Entry<String,Double> and collect into a Map of study-topics to average grade.

Exercise 2: CSV to Objects

CSV to Object Stream

Reading students from disc,[2] or any other kind of object for that matter.

In this task you will write a small utility class that will help you to test things and is also useful to load data from files and e.g. put them in the application or mock data source. The use case of this utility class is to read data from files and turn them into real (Java) objects.

The class is a CSVObjectReader and has one constructor and two methods, stream(…​) and asList(…​).

Of course, you start with tests. To do that, you will find a student.csv file in the NetBeans-IDE project.

The test should:

  • For the constructor call use Factories::createStudent as creator. This is a method reference. See lambda theory if you forgot what that means. From the String "students.csv", create a Path java.nio.file.Path using the Paths utility class in the same package.

  • Both stream and asList methods take two parameters:

    1. A function Function<? super String[], ? extends T> creator that transforms an array of strings into an object of type T, and

    2. a Predicate<? super String[]> called rowFilter that ensures that the array is fit for purpose.
      Example: the first string in the array of strings contains only digits and is the student number.
      The asList method uses the stream method, and collects the stream into a list.

The testing part should do the following:

  • Collect the resulting stream in a list and assert that

    • The list is not null.

    • The list is not empty.

    • The list has 50 elements.

    • The list contains a student with the name "Olympia Oliphant" at index three.
      Use the assert that extracting method with a fieldname as parameter. See the assertJ API and doc for examples.

  • In all the asserts, use a message and make sure you put the arguments to the assertThat method chained in the proper order.

You can use it as is shown in the listing below

Show that it works
To show the use of this class, create a csv file, of take it from an earlier project, and define a type, say student with studentnumber, name, birthdata, and gender.

To top the demonstration off, add a filter function that accepts a list of students, which streams said list and filters out students by some rule, e.g. by gender and or age.

A Helper class called Factories might be useful in your demo. Factories in our solution has two methods, one to create a student from an array of String, as is required by the CSVObjectStream class and one method to turn object back into csv records. We call the last one studentAsCSVLine. For the remaining few details look at the class diagram below.

You may want to keep this project and reuse it in other exercises.

Note that the Student test class is a static inner class in the factories, which is only available in the test source tree.

To complete the exercise, add a separate Main file that demonstrates the working of your module.

Using the csvobject utility class.
CSVObjectStream<Student> os
            = new CSVObjectStream( Student.class, Paths.get( "students.csv" ) );

        os.stream( Factories::createStudent,
                    r -> r[ 0 ].matches( "\\d+" ) )
            .forEach( System.out::println );
Figure 3. class diagram csvobjectstream

Exercise 3: Lambda Library

Lambda Library

Within this assignment you will get more familiar with the syntax of lambda expressions,as well as how to apply them. Furthermore you will use the new streams to get some work with collections done in a smart way.

Note: to run the FX application, don’t click "run" in Netbeans. Assuming you are using a sebipom based project, then first enable the fx profile , then right click the project and either

  • select 'Run maven' and select exec or

  • in the Navigator, right-click the 'javafx run' entry and select 'execute goal'.

The FX application works out of the box and you should not touch any of the FX parts. You will only work on the library itself.

Your task is to implement the business logic for a library. This library provides some methods to get access to the books and to retrieve several views on that books. A simple swing-GUI which displays the books of the library is given. You can use that to see parts of your implementation in action.

As usually, you will find an NetBeans project for this assignment inserted in you subversion repository. Note that the project will run, but not all functionality is implemented, yet. Proceed in the following steps:

  1. Start working on this assignment by studying the Javadoc which is given. Take especially care of the classes LibraryModel and SearchStrategy, since they will form the requirements you will have to implement.

  2. Implement one of the test cases in the test class LibraryModelTest. A good starting point is the testGetBooks() method.

  3. Implement the method for which you wrote the test(s) in the previous step.

    • It may need more tests if a method has more aspects to fulfill. Each aspect could have a different test.
      It is quite common to have more test methods per business method.

  4. Repeat the steps 2 and 3 until all functionality is implemented.

Note that this task has a dependence on the previous task cvsobjects, to read a csv file as book objects.

Additional information on lambda expressions and the java 8 features can be found at the following locations:

javadoc of the application. This provides the requirements of all the methods to be tested and implemented. You can derive your test data from the `library.csv} file.

Update The provided test class has two methods called book(int) and books(int …​), where the parameter is called id or ids. This could lead to confusion, when you interpret that as the id of the book in the csv file. It is not that number but simply the index in the List of books, so starts at 0, not 1.

1. A British synonym for mine is pit, that is why you have the bird with the helmet as logo
2. Terry Pratchett would have enjoyed this one