CS636 Class 10: more notes on pa1, JUnit (handout), transactions

Handout: JUnit Tutorial

Examples Of methods in domain classes other than getters and setters

Ex: In PizzaOrder, names for status values are provided String status String()

BigDecimal for money, to keep pennies right.  See Representing Money.

                               price * quantity

                               for multiplication, need to use BigDecimal method not “*”

Ex: LineItem has BigDecimal calculateItemTotal()

public BigDecimal calculateItemTotal() {

      // We can't use * to multiply with BigDecimal, but it knows how--

      BigDecimal total = product.getPrice().multiply(new BigDecimal(quantity));

      return total;

}

DAO :problem of inter-related objects

Ex: from pizza:

                2 finders for PizzaOrders

                Set<PizzaOrder> find OrdersByRoom(…) – full details: Toppings, PizzaSize

                Set<PizzaOrder> find OrdersByDays(…) – no details! P.O. object has null size ref, null Set<Toppings>

                (only used by code that doesn’t need details)

Last time looked at object graphs for Music, saw that starting from Download, we need a User and Track object to fill in details.

Starting from Invoice, quite a lot of objects are referenced directly or indirectly: User, LineItems, then from LineItem, Product, and from Product, Tracks

If start from User, nothing else is ref’d.  And so on.

We can use null ref’s to indicate missing detail objects.

Note that if we had bidir relationships everywhere, we would have even more objects ref’d from each object, and it would be hard to know where to draw the line and null-out further refs.

So that’s an additional “cost” to additional relationships in the domain classes: needing to decide where to cut them off.

Goal for pa1: nice service API

So write stub methods of service API if no time to implement them.

If not enough time, skip AdminApp

But get SystemTest working in full if you can, and UserApp.

More notes on pa1--

Domain objects code can use ref’s to other domain objects especially if they are details of this object.

Product code can use its Tracks

Invoice owns its LineItems

Domain code is the code of the “model”.

Service layer is in charge, but does a lot of work by calling the domain objects’ methods.

End of discussion directly relevant to pa1

JUnit

We’re using JUnit 4, which uses annotations, much neater than the older JUnit 3, but same capabilities.

Annotations in Java : see Wikipedia Java_annotation, Java tutorial

Built-in annotations : notes to the Java compiler

@Override - Checks that the method is an override. Causes a compile error if the method is not found in one of the parent classes or implemented interfaces.

Example: PizzaTopping.java:

        @Override
      public int hashCode()
      {
        return getToppingName().hashCode();
      }

Others :

Non-built-in annotations: notes to other code processors

JUnit: @Test marks a method for JUnit to execute. It doesn’t affect how that code is executed.

Coming up: @Entity, etc. for JPA

See links from class web page to JUnit site with another tutorial, FAQ, Javadoc, etc.

see handout of first example, the calculator with a bug, and its unit test.

Note : this example is available as a project, linked from the class web page under Resources>JUnit

Also links to  a JUnit site with another tutorial, FAQ, Javadoc, etc.

How a JUnit test Executes

<Picture of two objects, one the object under test, of class Calculator, the other the tester object, of class CalculatorTest, also JUnit itself with ref to CalculatorTest.>

The test code in CalculatorTest calls the methods in Calculator, and JUnit arranges this setup and success/failure reporting from it, using the annotations as a guide for what to do.

How JUnit runs the JUnit test:

@BeforeClass method executes

For each @Test method in the class:

@Before method executes:   ß bring object(s) to known starting state

The @Test method executes  ß an experiment from that state

@After method executes

@AfterClass method executes

Note how JUnit is smart about exceptions. A unit test should not need a try/catch to test code that can throw exceptions. Look at testCreateTopping in PizzaOrderTest1.java as an example, another method there that expects an exception.  Neither needs try/catch in the test method.

Note another technology: assertions. These are checks that are put in the regular source code but only get executed if –ea is used in the run “java –ea …”.  They are useful when unit testing is hard to do because of dependencies.

Quick example:   assert r<=RMAX: “r out of bounds”;  // after calc. of r

But unit testing is currently more popular than assertions in the Java world, and is believed to be healthy for the code because it promotes more independent objects, control of dependencies, “cleaner” code.

Handling Dependent Objects in Tests

The hard part of unit testing is dealing with dependent objects. One way is to use “mock objects”, objects of a class that implements the same interface as the actual dependent, or is a subclass of it.  The implementation can be fake, that is, it can know what the test will ask of it, and just cook up a return for that.

We have HSQLDB as a wonderful mock DB, so we can test our DAOs with it, and once they are tested, test our service objects. We see that layering is helpful for testability.

Pizza1’s PizzaOrderDAOTest1, a JUnit 4 test using HSQLDB as a mock

Look at PizzaOrderTest1.java in dao package—

We’re using HSQLDB as a mock database here, simplifying our work immensely.

The @Begin method creates db (the DbDAO object) and pizzaOrderDAO objects (the « fixture « )  separately for each test

(We could move db object creation to @BeforeClass)

Transactions

Need for (multistatement) transactions.

Currently we are using auto-commit – JDBC default.

Auto-commit means Commit each statement.

If we need to bundle statements together for a certain action, we need to turn off auto-commit & start using conn.commit() and conn.rollback().

conn.setAutoCommit(false);

 Concurrent access to DB without transaction can cause data problems

Standard ex: lost update: 2 concurrent processes both adding to an account, only one increment shows (R = read, W=write)

R old amt

Add delta1                          R old amt

W new amt                         Add delta2

                                           W new amt – overwrites other’s  update                

Ex: Suppose in pizza we had code for new Topping id: find max(id) in toppings, use id+1 in insert

2 admins adding a Topping can get same new id causing failure of one (PK violation)

R old ids                               R old ids

Add one                              add one

-------------------------------------------

               Same new ids

 In fact, the code in pizza1 uses the nextid from pizza_sys_tab to generate the next topping id. This approach can still have a problem, but it's easier to fix because we only need to lock one row, not the whole table to prevent problems.

DB transactions were invented to solve these problems