This lesson is being piloted (Beta version)

Software reliability in Java - Testing and Exceptions

Overview

Teaching: 60 min
Exercises: 0 min
Questions
  • How do we use tesing in Java?

  • How do we use exceptions in Java?

Objectives
  • Explain the assertion and fault tolerance are built into software design.

  • Provide examples to demonstrate how JUnit implements unit testing in Java.

Pi j4.2 software-reliability from mcollison

Group Project

Pong is a game like pinball centred around a ball bouncing around the screen and the player has to keep the game ‘live’ for as long as possible using the paddle which can be moved side to side at the bottom of the screen and bounces the ball back into play off the side and top walls. If the ball reaches the bottom of the screen and passes the paddle the game ends.

Pong is an ideal game to implement with objects because it applies many of the constructs we’ve used so far in a tangible way. We’re going to tackle developing this game in three phases. Each phase will require your team of four to split into pairs and practice paired programming.

  1. Visualise the shapes.
  2. Make the ball move.
  3. Create the rules of the game.

Start by creating a new project that you all have access to and create a place for shared source code. Ideally this would be a source controlled project on a public hosting site like Github but for the interest of time OneDrive or any fileshare system will work fine.

Visualise the Shapes

Split into two pairs; two people will write a TestRectangle, TestCircle and TestMain classes and the others will write the Rectangle and Circle classes. The instructions for the classes are provided the tests will need to be created as you think appropriate but stick to Junit 4 as this is what was taught in the slides.

First we will need to create the shapes. The game will remain as simple as possible so we’ll just need Rectangles and Circles. Use inspiration from the previous projects you’ve built but create new abstract Shape class and concrete Rectangle and Circle classes. Only add the height, width and position attributes for now (and the constructors and getters/setters); don’t worry about implementing overlaps as that will come later.

Once you’ve create the abstract shape classes, create a separate Main class that contains an ArrayList of Shapes then create some four Reactangles and one Circle before adding them to a static ArrayList of Shapes with an identifier gameObjects. Once you’ve done this print out the properties of the Shapes using the toString() method. Remember, your IDE will generate a better toString method if you right-click generate and select all class attributes.

The printed information is useful but not visual enough for gameplay. Combine back together as a team and use the two classes below to help you visualise the Shapes ArrayList that you created in the Main. Create a UI class and a PongCanvas class in your project. Adjust the size and position of the gameObjects so the four Rectangles create a box around the edge of the window.

package mcollison.pong;

import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
/**
 * A class containing the user interface components to enable keyboard inputs and display of the graphics through a
 * configured window.
 * @author Matt Collison
 * @version 1.0
 */
public class UI {
    PongCanvas canvas;// the custom drawing canvas (class extends awt Canvas)
    JFrame frame;
    public UI() {
        canvas = new PongCanvas();
        canvas.setSize(400, 410);
        frame = new JFrame("Pong");
        frame.setPreferredSize(new Dimension(400, 400));
        frame.add(canvas);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
package mcollison.pong;

import java.awt.Canvas;
import java.awt.Graphics;
/**
 * A class extended from awt Canvas to handle the drawing of graphics.
 * @author Matt Collison
 * @version 1.0
 */
public class PongCanvas extends Canvas {
    /**
     * The paint method displays the objects on the first frame.
     * @param g the graphics object integrated in the UI from awt.
     */
    public void paint(Graphics g) {
        for (Shape s : Main.gameShapes){
            if (s instanceof Rectangle) {
                g.fillRect(s.getPosition()[0], s.getPosition()[1],
                        s.getWidth(), s.getHeight());
            } else if (s instanceof Circle) {
                g.fillOval(s.getPosition()[0], s.getPosition()[1],
                        s.getWidth(), s.getHeight());
            }
        }
    }


}

Make the ball move

At this stage you should have something that looks like frozen pong without the paddle. The next challenge is to make the ball move. TO achieve this we’ll create a Movable interface and a sublass of Circle called Ball that implements Movable.

First create the Movable interface that declares a move method. An example is provided below.

package mcollison.pong;

/**
 * An interface to note objects that move and change direction on collisions.
 * @author Matt Collison
 * @version 1.0
 */
public interface Movable {
    /** A method that will be called on each game loop. */
    public void move();
}

Next, implement that Movable interface in Ball. The class declaration is provided below. Continue to implement the move method within Ball, note you will need to update the position attribute of the super class from the move method and the amount you change affects the speed and direction of the ball - check back at the dimensions of the canvas for a hint on scale.

public class Ball extends Circle implements Movable 

Once you have the move method implemented it’s time to make an animation. Add the method below to your PongCanvas class.

    /**
     * The render method calls the 'move' method and creates the graphic for all Movable objects in their new
     * position using the java.awt.Canvas repaint method.
     */
    public void render() {
        for (Shape s : Main.gameShapes){
            if (s instanceof Movable ){
                repaint(s.getPosition()[0], s.getPosition()[1],
                        s.getWidth(), s.getHeight()); // Clear old area to background
                ((Movable) s).move();
                repaint(s.getPosition()[0], s.getPosition()[1],
                        s.getWidth(), s.getHeight()); // Add graphic at new position 
            }
        }
    }

The render method uses the repaint method to update the onscreen graphic of your objects after it has been move() d. The problem, however, is that the render method needs to be called each time you want a new frame. This loop is sometimes called the game loop. Update the main method in your Main class to render the Movable objects after a 50 millisecond delay.

        while (true) {
            try {
                //A hard delay between frames
                Thread.sleep(50);
                //move objects and render next frame
                ui.canvas.render();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

At this stage you should have a stationary box made from four triangles and a ball drifting across it. Well done you’ve made the ball move. Next we need to process the physics of collisions.

Create the rules of the game

The key part of the game that is missing is setting the rules for how game objects interact with eachother. At this stage hopefully you will recognise that we need to consider is any of our shapes collide. Implement the overlaps method in Rectangle and Circle. Note, you will likely want to assume a ‘bounding box’ for circle interactions. Assuming the rectangles don’t tile we can assume a circle will overlap at the same point as it’s bounding box. Alternaticely you can use the instanceof method to detect the nature of the comparitor Shape.

Once you have an overlaps method you’ll want to revisit the game loop in the Main class. Add a for loop that detects if any objects overlap and prints out the details of overlapping objects. Note you might want to restrict the checks to Movable objects interacting with all objects rather than repeatedly checking on non-movable objects.

At this stage the ball collisions with the wall should be detected but no action is taken. Clearly we want the ball to bounce off the walls but we don’t have a mechanism to change the behaviour of the move. Add a direction attribute to Shape so all instances of shapes have it and use this attribute in move(). Now, when a collision is detected update the direction attribute. Note, you will need to know where the collision has taken place to be able to change difection of the moving object in the correct way.

Now we finally have the ball bouncing around the window. However there is no user input, no paddle and no way to win or lose. Add a Triangle called paddle. Use a method like the one below which you should add to your UI class to change the position of the paddle object. Consider if the paddle should be Movable as it could bounce itself off the screen. Make sure paddle is added to the gameObjects else it will not be included in the collision detection.

    /**
     * A private subroutine called only from within the constructor to isolate the logic behind keyboard inputs.
     * @param game contains all the game objects and physics objects including those that can be controlled by the user.
     */
    private void keyboardInput(GamePhysics game){
        frame.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent evt) {
                switch (evt.getKeyCode()) {
                    case KeyEvent.VK_LEFT:
                        canvas.repaint(game.paddle.getPosition()[0],
                                game.paddle.getPosition()[1],
                                game.paddle.getWidth(),
                                game.paddle.getHeight());
                        game.left();
                        canvas.repaint(game.paddle.getPosition()[0],
                                game.paddle.getPosition()[1],
                                game.paddle.getWidth(),
                                game.paddle.getHeight());
                        break;
                    case KeyEvent.VK_RIGHT:
                        canvas.repaint(game.paddle.getPosition()[0],
                                game.paddle.getPosition()[1],
                                game.paddle.getWidth(),
                                game.paddle.getHeight());
                        game.right();
                        canvas.repaint(game.paddle.getPosition()[0],
                                game.paddle.getPosition()[1],
                                game.paddle.getWidth(),
                                game.paddle.getHeight());
                        break;
                    case KeyEvent.VK_B:
                        game.gameShapes.add(
                                new Ball(10, new int[]{50, 50})
                        );

                }
            }
        });

    }

Now you will have a controllable paddle and the game is starting to ressemble Pong but you’re missing a win or lose state. Create a new Rectangle called endZone and change the collision detection so that if ednZone is overlapping a movable object the game loop breaks and the game stops.

if ( s2 instanceof Paddle ){
  points++;
}
if ( s2 == endZone ) {
  return false;
}

At this stage you should have a functional application that runs pong. Congratulations!

Consider the following extension challenges:

  1. try to extend the game to enable points to be calculated by how many times you bounce the ball back into play (there is a hint in the code above).
  2. Speed up the game by changing the delay and size of the move steps.
  3. Add additional balls when B is pressed during gameplay.

I hope this workshop is satisfying and you reach the reward of making your own Pong application. If you didn’t, here’s one I made earlier… solution link

Key Points

  • The software development lifecycle in Java provides a framework for reliable, performant and professional production of code.