Unit Testing Vaadin?

I see a number of posts here asking how to test vaadin UI code and the general response seems to be to unit test your busines logic and use TestBench for your UI.

Given that Vaadin is fully java, it would seem very useful to be able to instantiate your application in a test and then interrogate it to assert that the correct components have been added and respond to click events in the correct way.

This is quite commonly done in the wicket framework, are there any examples of these kinds of tests for Vaadin?

thanks, Tom

Here is a quick example:

JUnit test

package com.example.tests;


import java.util.HashMap;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.example.app.Calc;
import com.vaadin.ui.Button;

public class UITest {

	Calc app;
	
	@Before
	public void setUp() throws Exception {
		app = new Calc();
		app.init();
	}

	@After
	public void tearDown() throws Exception {
		app.close();
	}
	
	@Test
	public void addition() {
		clickButton(app.buttons.get("C"));
		clickButton(app.buttons.get("1"));
		clickButton(app.buttons.get("+"));
		clickButton(app.buttons.get("2"));
		clickButton(app.buttons.get("="));
		Assert.assertEquals("3.0", app.display.toString());
	}
	
	// We really should have click() method in button!
	private void clickButton(Button button) {
		HashMap changes = new HashMap();
		changes.put("state", Boolean.TRUE);
		changes.put("mousedetails", "0,0,0,false,false,false,false,0,0,0");
		button.changeVariables(this, changes);
	}

}

The tested application


package com.example.app;

import java.util.HashMap;

import com.vaadin.Application;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Window;

public class Calc extends Application implements ClickListener {
	  // All variables are automatically stored in the session.
    private double current = 0.0;
    private double stored = 0.0;
    private char lastOperationRequested = 'C';

    // The operations for the calculator in the order they appear on the
    // screen (left to right, top to bottom)
    private String[] operations = new String[]
 { "7", "8", "9", "/", "4", "5", "6",
            "*", "1", "2", "3", "-", "0", "=", "C", "+" };


    // User interface component references left public to make accessing them from test easy. Might not be the best practice.
    public final Label display = new Label("0.0");
    public final HashMap<String, Button> buttons = new HashMap<String, Button>();

    /*
     * Application.init is called once for each application. Here it creates the
     * UI and connects it to the business logic.
     */
    @Override
    public void init() {
        // Create the main layout for our application (4 columns, 5 rows)
        final GridLayout layout = new GridLayout(4, 5);

        /*
         * Create the main window for the application using the main layout. The
         * main window is shown when the application is starts.
         */
        setMainWindow(new Window("Calculator Application", layout));

        // Create a result label that over all 4 columns in the first row
        layout.addComponent(display, 0, 0, 3, 0);

        for (String caption : operations) {

            // Create a button and use this application for event handling
            Button button = new Button(caption);
            button.addListener(this);

            // Add the button to our main layout
            layout.addComponent(button);
            
            buttons.put(caption, button);
        }
    }

    // Event handler for button clicks. Called for all the buttons in the
    // application.
    public void buttonClick(ClickEvent event) {

        // Get the button that was clicked
        Button button = event.getButton();

        // Get the requested operation from the button caption
        char requestedOperation = button.getCaption().charAt(0);

        // Calculate the new value
        double newValue = calculate(requestedOperation);

        // Update the result label with the new value
        display.setValue(newValue);

    }

    // Calculator "business logic" implemented here to keep the example minimal
    private double calculate(char requestedOperation) {
        if ('0' <= requestedOperation && requestedOperation <= '9') {
            current = current * 10
                    + Double.parseDouble("" + requestedOperation);
            return current;
        }
        switch (lastOperationRequested) {
        case '+':
            stored += current;
            break;
        case '-':
            stored -= current;
            break;
        case '/':
            stored /= current;
            break;
        case '*':
            stored *= current;
            break;
        case 'C':
            stored = current;
            break;
        }
        lastOperationRequested = requestedOperation;
        current = 0.0;
        if (requestedOperation == 'C') {
            stored = 0.0;
        }
        return stored;
    }

}

Unfortunately
http://dev.vaadin.com/ticket/4530
is still in backlog and thus clicking buttons from server-side is cumbersome. Otherwise UI layer jUnit tests should be quite easy for the most part.

Work around:
//get Button from component
Button button = someComponent.getButton();
//create new event
ClickEvent event = button.new ClickEvent(button);
//click button
someComponent.buttonClick(event);

assert here what you are expecting as e result.

Antoni

Hi Joonas,

your Calc-example and the test for it looks great.
Unfortunately, it is written for Vaadin 6 and does not work with Vaadin 7.
Could you please post here a version of your test-example that works for
Vaadin 7 and does not require Vaadin’s Testbench API, for which one has
to pay ?

Thanks in advance,
Thomas

The Karibu Testing framework may be exactly what you’re looking for. It supports both Vaadin 8 and Vaadin 10; adding Vaadin 7 support is very easy. https://github.com/mvysny/karibu-testing