Showing posts with label test-driven. Show all posts
Showing posts with label test-driven. Show all posts

1 August 2023

Shut up and write a test!

Due to the hype around ChatGPT I wanted to create this text with it. I failed. I tried several versions of different prompts, and also asked the Bing variant, but it failed to create any text I liked. It could not simulate my personal style. I should have to train a model with all my blog posts ... It seemed easier to use with the good old method of writing my texts myself.

Should I Write a Test?
Around ten years ago I stumbled upon a diagram if and when to write a test. This was a repeating question in trainings, when should you write tests? Before or after writing the code? Only when you have time? Only when you feel like it? I loved the diagram because there was only one end state. Whatever the reason or excuse would be, just shut up and write a test! It does not matter if you are tired, or if it is a little change, it should be tested. Such a rule is helpful when you want to adopt Test Driven Development, as there are plenty of excuses why to skip it.

Shut Up and Write a Test!
The original diagram was created by Gemma R Cameron, a software engineer and event organiser from Manchester. She was inspired by a similar diagram about skating. Recently, when I was asked again when to test, I wanted to share the diagram and could not find its page. I thought it lost and planned to repost the diagram here. Eventually I discovered Gemma's page again, it had moved from a .me to a WordPress domain. I am glad that the original source is still available. You can find it here.

Java Version
The original version of the diagram was for Ruby, indicated by the use of Automock. What was bad with Automock? It provided some support for automated mocking of Rails application and is dead since 2016. Here I present a slightly modified version for Java, replacing Automock with PowerMock. And I do know what is wrong with PowerMock. It is a powerful mocking tool which uses a custom class loader and bytecode manipulation to enable mocking of static methods, constructors, final classes and methods, private methods and more. It is useful to bring legacy code under test, before seams can be introduced. The bytecode manipulation makes it slow compared to regular mocking. If used often, the whole test suite will run much longer than needed. Further, if code is hard to test, I change its design to make it testable by applying SOLID principles or by creating seams as outlined by Michael Feathers in his WELC book. Using PowerMock for tests does not improve the design. (I am not inherently against PowerMock, it is extremely powerful and as such dangerous. I needed something to replace Automock.)

Download and print the Java version of the Shut up and Write a Test Diagram. The fact that there are versions of this diagram for both Ruby and Java is a testament to the universality of its principle: Shut up and Write a Test! The Code Cop approves, and I am sure your future self will too.

30 August 2020

TDD a UI with Fast Tests

AndroidsThis is another part of my series of TDD-ing user interfaces. Earlier this year I looked at test driving classic, fat, state based UIs like Swing or WinForms and then web component libraries like Vaadin. In both situations it was possible to TDD the UI using the Model View Presenter (MVP) pattern and a decent test bed for testing the implementation of the view. Later I explored test driving an immediate mode GUI, which was even easier than doing so for retained mode: There was no need to search for components, capture events or trigger events. This time I want to experiment with test driving a user interface by only using UI tests. In theory I could do that with Swing or Vaadin, but the UI tests of these technologies are too slow. For my TDD cycle I want fast tests!

Android UI Tests
My friend Bastien David told me that Android UI tests are pretty fast and I talked him into running this experiment with me. I had never done any Android development and had only little knowledge of the Kotlin language - it is good to have friends who know. Bastien's Kotlin/​Android starting point used Espresso and Robolectric for testing the Android UIs. The sample test
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.activityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class HelloActivityTest {

  @get:Rule
  var activityScenarioRule = activityScenarioRule<HelloActivity>()

  @Test
  fun hello_activity_has_some_hello_text() {
    onView(withId(R.id.text_hello)).check(matches(withText("Hello!")))
  }

}
used Espresso's matchers to find the text "Hello" on the view of the HelloActivity.

First Few Tests
We worked on the same exercise used in my previous articles, the Login Form exercise. The first tests for UI elements of the LoginActivity, i.e. user name field and login button were
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {

  @get:Rule
  var activityScenarioRule = activityScenarioRule<LoginActivity>()

  @Test
  fun `has username field with max length of 20`() {
    onView(withId(R.id.username))
      .check(matches(checkMaxLength(20))) // custom matcher
  }

  @Test
  fun `has label for username`() {
    onView(withId(R.id.username_label))
      .check(matches(withText("Phone, email or username")))
  }

  @Test
  fun `has login button`() {
    onView(withId(R.id.login_button))
      .check(matches(withText("Log in")))
      .check(matches(not(isEnabled())))
  }
}
These tests did not create any code, we declared the UI elements in the layout app/​src/​main/​res/​layout/​activity_login.xml to make each test pass. On Bastien's machine the tests were fast enough and it was easy to drive UI elements and their attributes. We had to create some custom Hamcrest matchers though, e.g. checkMaxLength. We decided not to go deeper and assert colours, styles or positions, but we could have.

Test Driving Logic
The next test
  @Test
  fun `when username is introduced then login button is enabled`() {
    onView(withId(R.id.username))
      .perform(typeText("a real username"))

    onView(withId(R.id.login_button))
      .check(matches(isEnabled()))
  }
brought a bit of logic into the LoginActivity,
class LoginActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_login)

    username.addTextChangedListener {
      login_button.isEnabled = true // new logic
    }
  }
}
We only spent two hours on the exercise and did not get far. Still I could see where we would end. We would inspect every behaviour by its effect on the UI. While we could use the MVP pattern, we did not as the tests were fast enough.

Similar Technologies, e.g. React
Some modern UI technologies come with a fair amount of testing support. For example the same approach should be possible with React. There is a JavaScript/​React starting point in the Login Form Kata. Some people have tried that. I do not know if they went far enough for a definite conclusion.

David Tanzer dedicated some of his React TDD videos on testing the UI: In Part 1: Testing the UI Itself he explains the necessary setup to test React and in Part 2: Value and Cost of Tests he talks about possible test cases. He does not check text elements and style of the UI as he considers these things to have little probability of breaking later. Doing TDD, he looks for tests which influence the design of the code. I highly recommend you watch all of his videos.

Conclusion: Is this still TDD?
Test driving a UI with fast tests like Android or React is certainly possible, maybe even easy. These tests are not unit tests. Is it still TDD? We definitely write the tests first so it is Acceptance test driven (A-TDD) or at least "UI specification test driven". Unlike TDD these tests do not influence the design of the code because the components of the UI are usually specified by the requirements, e.g. in wireframes. As all tests exercise the code through the UI, there is no pressure on the code, its interfaces and collaborators. Still we can evolve the code because we have full test coverage and regression safety.

27 March 2020

TDD an Immediate Mode UI

Today I continue my experiments with test driving user interfaces. First I looked at test driving classic, fat, state based UIs like Swing or WinForms and later web component libraries like Vaadin. I need to explore different programming environments and platforms to see what is possible. In this post I want to have a look at immediate mode GUIs. An immediate mode GUI is a GUI where the event processing is directly controlled by the application. When I first read a similar sentence on Wikipedia, it did not help me at all. So what is an immediate mode GUI?

User InterfaceRetained Mode
A better question would be "what is retained mode?" Wikipedia states that retained mode is a style of API design in which the graphics library retains the complete object model of the rendering primitives to be rendered. That means that the widget, e.g. an instance of a JButton, contains all state needed to draw the button, i.e. colours, position, is the button clicked and so forth. When the button is created, or even "drawn", it is not causing the actual rendering. The GUI library decides when and how to render the widget and optimises the actual rendering. This includes double buffering, clipping or partial updates. Retained mode is the dominant style in GUI libraries, all user interfaces you have build were likely of this style.

Immediate Mode
So let's get back to Immediate mode. When using an immediate mode GUI library, the event processing is directly controlled by the application. There is no button object, there is just a Button(bounds Rectangle, text string) function which immediately draws the button with given text at the given position and size (argument bounds). The function returns true if the button was clicked. The application code must call all drawing commands required to describe the entire scene each time a new frame is displayed. This is often used in video games programming and examples of immediate mode rendering systems include Direct2D and OpenGL. If you want to know more about this mode, see this list of immediate mode gui tutorials on StackOverflow.

The (immediate mode) UI framework I am going to use is raylib, a simple and easy to use library to enjoy video games programming. See its cheat sheet for an overview of its functions. It has a simple API which hides everything regarding windowing system and environment. I am writing code in Go, so I am using raylib-go, the Golang bindings for raylib. Honestly I have no idea what I am doing. I have never used an immediate mode framework, not even heard of one before last year. In addition I know little to nothing about the Go language. Nevertheless I managed to talk my fellow crafter, Extreme Programmer and probably Vienna's longest time Go practitioner, Christian Haas into running this experiment with me. We spent one full day working on the Login Form exercise.

The first test: There is a button
import (
  "testing"
  ...
  rl "github.com/gen2brain/raylib-go/raylib"
)

func TestForm_LoginButton(t *testing.T) {
  var form login.Form
  ui := newTestingUI()

  form.Render(ui)

  if !ui.buttonCalled {
    t.Errorf("Button() was not called")
  }
}
Form is the struct containing the form's data, which is empty for now. Render is a receiver function on the form, which creates a button with no bounds and an empty text.
type Form struct{}

func (form Form) Render(ui FormUI) {
  ui.Button(rl.Rectangle{}, "")
}
To check that certain calls into raylib have been made, there is an interface FormUI between the application code and raylib. In the tests this interface is mocked to verify certain calls have been made. (In Go an interface type is defined as a set of method signatures. This is the way to achieve polymorphism.)
type testingUI struct {
  buttonCalled bool
}

func (ui *testingUI) Button(bounds rl.Rectangle, text string) bool {
  ui.buttonCalled = true
  return false
}
This follows an approach I have found as a possible TDD approach:
  • Design and write your methods separated from the actual UI.
  • TDD the elements and behaviour.
  • Mock single UI elements to verify necessary calls but do not show them.
For Swing I cannot see how this approach would be practical, but in this example with immediate mode raylib, it feels natural.

More Code
Soon the number of interactions with the UI made it necessary to add string ids to each drawing primitive. While raylib did not need them, other libaries do, so it did not feel wrong to add them. The mocked UI was growing. Here are the final pieces of code for the login button.
type testingUI struct {
  // verify if Button method has been called (mock)
  buttonCalled  map[string]bool

  // record button's text and bounds for later inspection (spy)
  buttonText    map[string]string
  buttonBounds  map[string]rl.Rectangle

  // return value of the Button method = user interaction (stub)
  buttonResults map[string]bool

  ...
}

func newTestingUI() *testingUI {
  ui := &testingUI{
    buttonCalled:  make(map[string]bool),
    buttonText:    make(map[string]string),
    buttonBounds:  make(map[string]rl.Rectangle),
    buttonResults: make(map[string]bool),
    ...
  }
  return ui
}

func (ui *testingUI) Button(id string, bounds rl.Rectangle, text string) bool {
  ui.buttonCalled[id] = true
  ui.buttonText[id] = text
  ui.buttonBounds[id] = bounds
  result := ui.buttonResults[id]
  ui.buttonResults[id] = false // reset button click after first call
  return result
}

func TestForm_LoginButton(t *testing.T) {
  var form login.Form
  ui := newTestingUI()

  form.Render(ui)

  if !ui.buttonCalled["login"] {
    t.Errorf("not found")
  }
}

func TestForm_LoginButtonText(t *testing.T) {
  var form login.Form
  ui := newTestingUI()

  form.Render(ui)

  if "Log in" != ui.buttonText["login"] {
    t.Errorf("is not \"Log in\"")
  }
}

func TestForm_LoginButtonBounds(t *testing.T) {
  var form login.Form
  ui := newTestingUI()

  form.Render(ui)

  expectedBounds := rl.Rectangle{300, 165, 110, 30}
  if ui.buttonBounds["login"] != expectedBounds {
    t.Errorf("expected %v, but was %v", expectedBounds, ui.buttonBounds)
  }
}
and the production code
type FormUI interface {
  Button(id string, bounds rl.Rectangle, text string) bool
  ...
}

func (form *Form) Render(ui FormUI) bool {
  buttonBounds := rl.Rectangle{X: 300, Y: 165, Width: 110, Height: 30}
  if ui.Button("login", buttonBounds, "Log in") {
    // TODO authenticate
  }

  return false
}
The third test, TestForm_LoginButtonBounds checks the position and size of the button. These properties are considered "layout". I do not like to test layout. I had to open GIMP to decide on the proper rectangle in expectedBounds, which I really dislike. I also expect this values to change a lot during initial development. Additionally Rectangle is a raylib type and so we depend on raylib in our code. Other options would have been:
  • Ignore layout completely. But then we would need to revisit all calls and add the Rectangles later.
  • Use abstract coordinates, i.e. map my coordinates into raylib Rectangles. That seemed like an extra overhead.
  • Move the responsibility of layout into the wrapper. There would be a button method for each button in the application and there would be more code outside my tests.
  • Move out the bounds and store them in the wrapper with a simple lookup on the id. Moving out stuff is against the nature of Immediate mode because the whole UI is expected to be in the code.
The wrapper for raylib is straight forward.
type RaylibFormUI struct{}

func (ui *RaylibFormUI) Button(id string, bounds rl.Rectangle, text string) bool {
  return raygui.Button(bounds, text)
}
This should give you an idea how things worked out. If you want to follow our TDD steps, here are the individual commits.

Is this MVP?
The MVP (Model View Presenter pattern) has a very thin, dumb UI, which is called the view. The model contains the UI data or UI model which might contain information about enabled fields, active buttons and so on. The presenter is a mediator and wires the model and the view. In the Go code above, the Form structure could be seen as an UI model. Later it will hold the user name and password data. The form receiver function func (form *Form) Render(ui FormUI) bool contains the presenter logic. It is not a separate object - there are no objects in Go - but it could be separate. Due to the immediate mode UI, there are no callbacks from the view in case of events. This removes the need for the usual MVP event listeners. The FormUI interface is like a MVP view as it hides the raylib dependency. It does not abstract away the underlying library, it is just a thin wrapper. It is not a MVP view. It could be made a view, i.e. provide more abstract functions in domain language, and then it would need tests on its own. In the experiment, this seemed unnecessary. In the end the FormUI will delegate many functions to raylib, so it could be generated from its original source code. This shows the tight coupling of the FormUI and the underlying UI library.

Water PressureConclusion
We built a login user interface following the requirements using Test Driven Development. It was easy and we did not face any problems, so no big deal. The Immediate mode library made it easier than for retained mode: There was no need to search for components, capture events, trigger events and so forth, definitely easier than my initial experiment. When comparing these experiments for Swing and raylib, I am reminded of the difference of Classic and Mockist TDD. In retained mode, e.g. Swing, I kept checking the state of UI components while in immediate mode I verified expected calls of the library. This approach allows variable depth of checks. We could have asserted colours and styles and we did not. So we will have to look at the finished form in the end. Christian proposed saving the image of the final view and storing it for regression testing.

We used TDD but there was little pressure on the design of the code. There was some pressure on the design of the API of Form, but there was no pressure on its internal workings nor on FormUI at all. The tests drove the creation of the UI - which was dictated by the requirements - there was no space for evolution. A different (UI) design might have been easier to test, but that was not an option. (A different UI would not have been different to test in this example anyway.) So we lost this particular benefit of TDD.

Try it yourself
I will continue my experiments and I would like to hear your ideas on the topic. The Go starting code with raylib-Go, its required dependencies and linter setup is available in the Login Form Kata. Try it yourself!

4 March 2020

TDD a Vaadin UI

As Code Cop I am working to get teams into adopting TDD. I use a mixture of classic training, guided workshops, Coding Dojos, code reviews and coaching to make this happen. One of my clients is working with Vaadin and keeps using it as an excuse to not use TDD. Not good. After my recent experiments with test driving user interfaces I "hijacked" one of our mob programming sessions and we worked on the Login Form Kata.

Vaadin
The client uses Vaadin 8 and the web has several ideas how to test (drive) it:
  • Use the Model View Presenter pattern, see Vaadin Advanced Application Architectures.
  • Gradually separate the UI from the logic, thinking of MVP more as a process than a pattern, starting with separate methods, which are accessible to tests so that the test can invoke them. For more details see Is MVP a Best Practice?
  • Create factories for all UI components so that tests can mock the UI. See this answer on StackOverflow for more details.
  • Running integrated tests in a test bed to simulate UI.​getCurrent() or VaadinSession.​getCurrent(), e.g. using the Karibu-Testing library.
LoginPresenter
We used MVP as described in my previous article. The group decided against a view model, because there was no specific UI model. Next was the presenter. The first two tests made us implement the requirement for a successful login: User name and password given, button "Log in" clicked, back end reports success, then the form is closed.
import org.mockito.Mockito;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class LoginPresenterTest {

  public static final String USERNAME = "Peter";
  public static final String PASSWORD = "Slovakia";

  private AuthenticationService authenticationService;
  private NavigatorView navigatorView;
  private LoginView loginView;
  private ExecutorService executorService;

  private LoginPresenter loginPresenter;

  @BeforeEach
  public void init() {
    authenticationService = Mockito.mock(AuthenticationService.class);
    navigatorView = Mockito.mock(NavigatorView.class);
    loginView = Mockito.mock(LoginView.class);
    executorService = Executors.newSingleThreadExecutor();

    loginPresenter = new LoginPresenter(authenticationService, navigatorView,
                                        loginView, executorService);
  }

  @Test
  public void should_call_backend_on_button_click() throws InterruptedException {
    Mockito.when(authenticationService.authenticate(USERNAME, PASSWORD))
      .thenReturn(new AuthenticationResult(true, "we do not care!"));

    loginPresenter.login(USERNAME, PASSWORD);
    waitForAuthentification();

    Mockito.verify(authenticationService, Mockito.times(1))
      .authenticate(USERNAME, PASSWORD);
  }

  @Test
  public void should_navigate_on_login_success() throws InterruptedException {
    Mockito.when(authenticationService.authenticate(USERNAME, PASSWORD))
      .thenReturn(new AuthenticationResult(true, "we do not care!"));

    loginPresenter.login(USERNAME, PASSWORD);
    waitForAuthentification();

    Mockito.verify(navigatorView, Mockito.times(1))
      .navigateToDashBoard();
  }

  private void waitForAuthentification() throws InterruptedException {
    executorService.shutdown();
    executorService.awaitTermination(1, TimeUnit.SECONDS);
  }
Using an ExecutorService made the asynchronous execution pretty simple. I like how they wrote two tests for the different aspects of successful login. Using smaller steps helped to get some logic sooner. It turned out that we needed another component for navigation, so we made up the NavigatorView. Then we went for the requirements User name and password given, button "Log in" clicked, back end reports an error, show message in error line, form stays open and While the back end is working, the "Log in" button stays disabled.
import org.mockito.InOrder;
import static org.mockito.ArgumentMatchers.any;

...

  @Test
  public void should_display_error_on_login_error() throws InterruptedException {
    String errorMessage = "error message";
    Mockito.when(authenticationService.authenticate(USERNAME, PASSWORD))
      .thenReturn(new AuthenticationResult(false, errorMessage));

    loginPresenter.login(USERNAME, PASSWORD);
    waitForAuthentification();

    Mockito.verify(navigatorView, Mockito.never())
      .navigateToDashBoard();
    Mockito.verify(loginView, Mockito.times(1))
      .displayErrorMessage(errorMessage);
  }

  @Test
  public void should_register_to_view() {
    Mockito.verify(loginView, Mockito.times(1))
      .addListener(any(LoginListener.class));
  }

  @Test
  public void should_disable_button_while_backend_is_working() throws InterruptedException {
    Mockito.when(authenticationService.authenticate(USERNAME, PASSWORD))
      .thenReturn(new AuthenticationResult(false, "we do not care!"));

    loginPresenter.login(USERNAME, PASSWORD);

    InOrder inOrder = Mockito.inOrder(loginView);
    inOrder.verify(loginView).setLoginButtonEnabled(false);
    waitForAuthentification();
    inOrder.verify(loginView).setLoginButtonEnabled(true);
  }
}
The finished presenter class looked almost like the one for Swing.
import java.util.concurrent.ExecutorService;

public class LoginPresenter implements LoginListener {

  private final AuthenticationService authenticationService;
  private final NavigatorView navigatorView;
  private final LoginView loginView;
  private final ExecutorService executorService;

  public LoginPresenter(AuthenticationService authenticationService,
                        NavigatorView navigatorView, LoginView loginView,
                        ExecutorService executorService) {
    this.authenticationService = authenticationService;
    this.navigatorView = navigatorView;
    this.loginView = loginView;
    this.executorService = executorService;

    loginView.addListener(this);
  }

  @Override
  public void login(String username, String password) {
    loginView.setLoginButtonEnabled(false);
    executorService.submit(() -> invokeLoginService(username, password));
  }

  private void invokeLoginService(String username, String password) {
    AuthenticationResult authenticate =
      authenticationService.authenticate(username, password);
    loginView.setLoginButtonEnabled(true);
    if (authenticate.success) {
      navigatorView.navigateToDashBoard();
    } else {
      loginView.displayErrorMessage(authenticate.message);
    }
  }
}
And the presenter would hide behind the LoginListener interface.
public interface LoginListener {

  void login(String username, String password);

}
Test Bed
As for the Swing version, using a test bed helped testing the view. There is a useable Vaadin testing tool, the Karibu-Testing library. It worked out of the box, no special treatment necessary. We wrote tests that the view forwards actions to the LoginListener, i.e. the LoginPresenter, and that the view can display an error message.
import com.github.mvysny.kaributesting.v8.MockVaadin;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import org.mockito.Mockito;

import static com.github.mvysny.kaributesting.v8.LocatorJ.*;

class LoginViewTest {

  private final LoginViewImpl content = new LoginViewImpl();

  @BeforeEach
  void beforeEach() {
    MockVaadin.setup(() -> new HelloUI(content));
  }

  @AfterEach
  void afterEach() {
    MockVaadin.tearDown();
  }

  @Test
  void should_pass_on_button_click() {
    LoginListener loginListener = Mockito.mock(LoginListener.class);
    content.addListener(loginListener);

    // simulate a text entry as if entered by the user
    _setValue(
      _get(TextField.class, spec -> spec.withCaption("Phone, email or username")),
      "peter");

    _setValue(
      _get(TextField.class, spec -> spec.withCaption("Password")),
      "Slovakia");

    // simulate a button click as if clicked by the user
    _click(_get(Button.class, spec -> spec.withCaption("Log in")));

    Mockito.verify(loginListener).login("peter", "Slovakia");
  }

  @Test
  void should_display_error_message() {
    content.displayErrorMessage("myError message");

    // simulate a text entry as if entered by the user
    Label label = _get(Label.class, spec -> spec.withValue("myError message"));

    Assertions.assertEquals(label.getStyleName(), "warning");
  }
}
Satisfying these two tests gave us the following view:
import com.vaadin.ui.*;

public class LoginViewImpl extends VerticalLayout implements LoginView {

  private final TextField username = new TextField("Phone, email or username");
  private final TextField password = new PasswordField("Password");
  private final Button loginButton = new Button("Log in");
  private final Label errorLabel = new Label();

  public LoginViewImpl() {
    addComponent(username);
    addComponent(password);
    addComponent(loginButton);
    addComponent(errorLabel);
    errorLabel.setStyleName("warning");
  }

  @Override
  public void addListener(LoginListener loginListener) {
    loginButton.addClickListener(clickEvent -> {
      loginListener.login(username.getValue(), password.getValue());
    });
  }

  @Override
  public void displayErrorMessage(String errorMessage) {
    errorLabel.setValue(errorMessage);
  }

  // ...
}
That was as far as we went during the sessions. We did not finish the whole login window, next tests would drive the logic of setLoginButtonEnabled(boolean).

Conclusion
Besides small differences, the code looks much like the Swing version. The process worked the same, most tests do not deal with the UI. As I do not know many UI frameworks my conclusion is limited. Maybe - hopefully - probably this approach works for state based user interfaces as long as there is a reasonable test bed. I can see how it could work for C# WinForms. On the other hand, Vaadin and WinForms are very similar to Java's Swing. I need to look at other kind of user interfaces.

7 February 2020

TDD Classic State Based UI

Most modern user interface technologies are state based. Classic frameworks like Swing, JavaFX, Eclipse RCP, Windows Forms, Vaadin and many more consist of heavy weight UI widgets that manage all their state and interact with the application using event handlers. Such frameworks are harder to test than plain code. I have not found much information on how to do that, one resource that got me started is chapter eight from Lasse Koskela's Test Driven. I am using many ideas from that book in here.

Model View Presenter (MVP) Pattern
Because these UI technologies are heavy weight, hard to test and often slow to run, one needs to decouple from them as much as possible. The goal is to minimize all UI dependencies. As few classes as possible should be "contaminated" with UI stuff, and the code that needs to do UI should contain as little logic as possible. Common ways to separate concerns between the interface and the underlying logic are MVC, MVP, MVVM or similar architectural patterns. I am using the MVP pattern. Its goal is - obviously - to facilitate automated unit testing and improve the separation of concerns in presentation logic. That means MVP is used to have a very thin, dumb UI, which is called the view. The model contains the UI data or UI model which might contain information about enabled fields, active buttons and so on. The presenter is a mediator and wires the model and the view.

Using Java and Swing
On my research on how to test drive user interfaces I started with Java and Swing. The Wikipedia page lists several UI frameworks which use the MVP patterns themselves, Swing being one of them. That means that Swing uses MVP internally. Larger components like JComboBox or JTable have their own model classes ComboBoxModel and TableModel. This is useful because the models do not depend on the user interface part of Swing and can be TDD'ed in the usual way. It does not mean that Swing can be test driven easily. As example I will run my Login Form Kata. I am going to develop the basic login screen step by step using TDD.

First Test: LoginModel should contain user lookup and password
I start with the model. The model contains the state of the user interface. There is no problem using TDD for that. My first test is
class LoginModelTest {

  LoginModel model = new LoginModel();

  @Test
  void shouldContainUserLookupAndPassword() {
    model.setLookup("user@server.com");
    model.setPassword("secret123");

    assertEquals("user@server.com", model.getLookup());
    assertEquals("secret123", model.getPassword());
  }
}
After seeing it red, I add
public class LoginModel {

  private String lookup;
  private String password;

  // getters and setters for lookup and password
}
Green. Usually I am not testing for getters and setters, but today I have to start somewhere. Depending on the used version of MVP, the model could update the view itself. I choose to keep the model very simple, following the Passive View aka Humble Dialog Box variant of MVP. This seems to be the usual way. In this example of login form, there is no state that goes back from the model into the view yet, e.g. if the login button should be disabled, so it does not make any difference.

Test: LoginPresenter should pass lookup and password to its model
Next is line is the LoginPresenter. The presenter is handling all user input. It is important, that the presenter has no dependency on Swing, too, so there is no problem TDDing that. The presenter will be notified of user input into the lookup or password field and will store the values in the model.
class LoginPresenterTest {

  LoginModel model = new LoginModel();
  LoginPresenter presenter = new LoginPresenter(model);

  @Test
  void shouldPassLookupAndPasswordToModel() {
    presenter.lookupChanged("user");
    presenter.passwordChanged("pass");

    assertEquals("user", model.getLookup());
    assertEquals("pass", model.getPassword());
  }
}

public class LoginPresenter {

  private final LoginModel model;

  public LoginPresenter(LoginModel model) {
    this.model = model;
  }

  public void lookupChanged(String newLookup) {
    model.setLookup(newLookup);
  }

  public void passwordChanged(String newPassword) {
    model.setPassword(newPassword);
  }
}

Empty LoginView
Finally I create the LoginView interface. The view wraps the UI technology completely. It starts as an empty interface. The methods to come will be driven by the needs of the presenter.
public interface LoginView { }

Test: LoginPresenter should close the view on successful login
It is time to go for some real logic. When the login button is clicked, the authentication back end will be called and if the call was successful, the view should be closed. The test uses Mockito to mock the view and verify it has been called.
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class LoginPresenterTest {

  LoginModel model = new LoginModel();
  LoginView view = mock(LoginView.class);
  AuthenticationService auth = mock(AuthenticationService.class);

  LoginPresenter presenter = new LoginPresenter(model, view, auth);

  @Test
  void shouldCloseViewOnSuccessLogin() {
    model.setLookup("user");
    model.setPassword("secret");
    when(auth.authenticate("user", "secret")).
      thenAnswer(invocation -> {
        return new AuthenticationResult(true, null);
      });

    presenter.loginButtonClicked();

    verify(view).close();
  }
}
This adds a close() method to the LoginView. Because I own the view, I can use domain names and the actual UI technology is not visible from the outside.

Test: LoginPresenter should display an error on failed login
Another test drives the display of an error message if authentication fails. This creates a showError(String message) method in the view.
  @Test
  void shouldDisplayErrorOnFailedLogin() {
    model.setLookup("user2");
    model.setPassword("secret2");
    when(auth.authenticate("user2", "secret2")).
      thenAnswer(invocation -> {
        return new AuthenticationResult(false, "Login failed.");
      });

    presenter.loginButtonClicked();

    verify(view).showError("Login failed.");
  }
And so the loginButtonClicked method is complete.
  public LoginPresenter(LoginModel model, LoginView view,
                      AuthenticationService authenticationService) {
    this.model = model;
    this.view = view;
    this.authenticationService = authenticationService;
  }

  @Override
  public void loginButtonClicked() {
    AuthenticationResult result =
      authenticationService.authenticate(model.getLookup(), model.getPassword());
    if (result.success) {
      view.close();
    } else {
      view.showError(result.message);
    }
  }

Test: LoginPresenter should call Authentication service asynchronously
As stated in the requirements, all calls to the back end need to be asynchronous. Synchronous code would block the Swing event thread and render the UI unresponsive. So I change the tests to force the calls to be asynchronous. I need two java.​util.​concurrent.​CountDownLatchs and have to wait on them to start and finish asynchronous processing.

Test Bed
The last piece is the view - the implementation of the view to be precise. To test the real view it has to be started and sent some events. This is what I wanted to avoid. It is slow and brittle. By following the MVP pattern described above, the view will have as little code as possible. There will only be a few tests executing it. Sometimes it is necessary to call certain methods on UI classes for the UI to work at all, e.g. show. It is not pretty. Best would be to use a test bed or harness to run these UI components in an isolated way, e.g. to have a dedicated window for the component under test and to create the application in a way that allows to exercise UI features independently.

Lasse Koskela mentions Abbot to test stand alone AWT or Swing components. It provides helper code for finding and interacting with UI elements. See how Abbot works in one of its tutorials. It is pretty old and its test support is for JUnit 3, but it gets the job done. It displays a frame while running the tests and there are flickering tests from time to time. This is not a problem of Abbot itself, but is the nature of full UI tests. As I said, these are brittle.

Test: SwingLoginView has a login button with text
A basic start is to assert the existence of UI elements. Abbot offers several ways to find elements, using its getFinder().​find(...) method. An easy way to locate elements is to use ids or names, e.g. with loginButton.​setName("LoginButton").
import javax.swing.JButton;
import javax.swing.JPanel;

import abbot.finder.ComponentSearchException;
import abbot.finder.matchers.NameMatcher;
import junit.extensions.abbot.ComponentTestFixture;

public class SwingLoginViewTest extends ComponentTestFixture {

  LoginView view = new SwingLoginPanel();

  public SwingLoginViewTest(String name) {
    super(name);
  }

  public void testHasLoginButtonWithText() throws ComponentSearchException {
    showFrame((JPanel) view); // Abbot shows the view
    JButton loginButton = findLoginButton();

    assertEquals("Log in", loginButton.getText());
  }

  private JButton findLoginButton() throws ComponentSearchException {
    return (JButton) getFinder().find(new NameMatcher("LoginButton"));
  }
}
This tests forces me to create the initial login panel.
import javax.swing.JButton;
import javax.swing.JPanel;

public class SwingLoginPanel extends JPanel implements LoginView {

  private final JButton loginButton = new JButton("Log in");

  public SwingLoginPanel() {
    createLoginButton();
  }

  private void createLoginButton() {
    loginButton.setName("LoginButton");
    add(loginButton);
  }

  @Override
  public void close() {
  }

  // other empty LoginView methods

}

Observer Pattern
In MVP, the view delegates user input somewhere else and does nothing but rendering. We using the Observer Pattern to get notifications from the UI. The view takes the role of subject and the presenter is observing it. Observer Pattern subjects need to manage the list of observers - in Java usually named listeners - and allow code to register and sometimes deregister them.

Test: SwingLoginView should send button click to presenter
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import abbot.tester.JButtonTester;

...

  public void testSendButtonClickToPresenter() throws ComponentSearchException {
    LoginListener listener = mock(LoginListener.class);
    view.registerLoginListener(listener);

    showFrame((JPanel) view);
    JButton loginButton = findLoginButton();
    JButtonTester tester = new JButtonTester(); // from Abbot
    tester.actionClick(loginButton);

    verify(listener).loginButtonClicked();
  }
As I said, the presenter will be the actual observer, hiding behind the LoginListener interface.
public interface LoginListener {

  void loginButtonClicked();

}
Its implementation is straight forward.
  @Override
  public void registerLoginListener(LoginListener listener) {
    loginButton.addActionListener(ae -> listener.loginButtonClicked());
  }

Test: SwingLoginView has input fields for lookup and password
Like for the login button, I assert the existence of lookup and password fields and that they are wired to the listener. To verify that, the test code enters some text into the fields and checks that the listener mock has been called.
  public void testHasLookupField() throws ComponentSearchException {
    LoginListener listener = mock(LoginListener.class);
    view.registerLoginListener(listener);

    showFrame((JPanel) view);
    JTextField lookupField = findLookupField();

    assertEquals(20, lookupField.getColumns());

    // verify that it is wired as well
    JTextFieldTester tester = new JTextFieldTester(); // from Abbot
    tester.actionEnterText(lookupField, "user");

    verify(listener).lookupChanged("user");
  }

  private JTextField findLookupField() throws ComponentSearchException {
    return (JTextField) getFinder().find(new NameMatcher("LookupField"));
  }
Yes, this is not a good unit test as it tests two things - visible by its two checks, assert and verify. I should split that into two independent tests. Unfortunately these tests are slow, so I want to minimize their number. Together with a similar test for the JPasswordField passwordField the view is taking shape.
  private final JTextField lookupField = new JTextField(20);
  private final JPasswordField passwordField = new JPasswordField(20);

  private void createLookupField() {
    lookupField.setName("LookupField");
    add(lookupField);
  }

  private void createPasswordField() {
    passwordField.setName("PasswordField");
    add(passwordField);
  }

  @Override
  public void registerLoginListener(LoginListener listener) {
    lookupField.getDocument().
      addDocumentListener(new AllDocumentListener() {
        @Override
        protected void fire() {
          listener.lookupChanged(lookupField.getText());
        }
      });

    passwordField.getDocument().
      addDocumentListener(new AllDocumentListener() {
        @Override
        protected void fire() {
          listener.passwordChanged(new String(passwordField.getPassword()));
        }
      });

    loginButton.addActionListener(ae -> listener.loginButtonClicked());
  }
To be notified of each input character, I use Swing's DocumentListener. (AllDocumentListener is my own base class to handle the many methods of it.) This gives the final listener for the observer:
public interface LoginListener {

  void lookupChanged(String lookup);
  void passwordChanged(String password);
  void loginButtonClicked();

}

Test: SwingLoginView should display errors
This is the final test for the view.
  public void testErrorDisplay() throws ComponentSearchException, InterruptedException {
    showFrame((JPanel) view);
    JLabel errorField = findErrorField();

    assertEquals("", errorField.getText());

    view.showError("Alert!");
    Thread.sleep(10); // wait for asynchronous update

    assertEquals("Alert!", errorField.getText());
    assertEquals(Color.RED, errorField.getForeground());
  }

  private JLabel findErrorField() throws ComponentSearchException {
    return (JLabel) getFinder().find(new NameMatcher("ErrorField"));
  }
And the final SwingLoginView:
import java.awt.Color;

import javax.swing.JLabel;
import javax.swing.SwingUtilities;

...

  private static final Color ERROR_COLOR = new Color(255, 0, 0);
  private final JLabel errorField = new JLabel();

  private void createErrorField() {
    errorField.setName("ErrorField");
    errorField.setForeground(ERROR_COLOR);
    add(errorField);
  }

  @Override
  public void showError(String message) {
    SwingUtilities.invokeLater(() -> errorField.setText(message));
  }
SwingUtilities.invokeLater() is needed because showError is called asynchronously. While there is agreement in the TDD community to not assert on layout or styling, I am asserting that the foreground of the error field changed to an colour indicating the error, because it is important to indicate errors also in colour.

Test: LoginPresenter should register itself to the view
To complete the Observer Pattern, the presenter needs to register itself to the view. I started with verify(view).​registerLoginListener(​Mockito.​any(​LoginListener.class)); but that is not what I want. I really want that the methods on the listener trigger the required functionality in the back end or model.
import org.mockito.ArgumentCaptor;

...

  @Test
  void shouldRegisterItselfToView() throws InterruptedException {
    when(auth.authenticate(any(String.class), any(String.class))).
      thenReturn(new AuthenticationResult(true, null));

    ArgumentCaptor<LoginListener> argument = ArgumentCaptor.forClass(LoginListener.class);
    verify(view).registerLoginListener(argument.capture());
    LoginListener listener = argument.getValue();

    listener.lookupChanged("user");
    assertEquals("user", model.getLookup());

    listener.passwordChanged("pass");
    assertEquals("pass", model.getPassword());

    listener.loginButtonClicked();
    Thread.sleep(10);
    verify(auth).authenticate("user", "pass");
  }
When the presenter registers itself to the view, I capture the listener using Mockito's ArgumentCaptor. Then I call the listener and check the wanted behaviour. The sleep time is required because authentication is run asynchronously. Finally the presenter is complete.
public class LoginPresenter implements LoginListener {

  // ...

  public LoginPresenter(LoginModel model, LoginView view,
                        AuthenticationService authenticationService) {
    this.model = model;
    this.view = view;
    this.authenticationService = authenticationService;

    view.registerLoginListener(this);
  }

  // ...
}
and the final view (interface) is
public interface LoginView {

  void close();
  void showError(String message);
  void registerLoginListener(LoginListener listener);

}
The actual commits are here.

Conclusion
This was my first try and it worked well. I conclude that it is possible to TDD UIs at least using Java and Swing. I have working examples and the testing tools are fair. The final code has the usual TDD benefits - more separation between concerns (i.e. logic and UI technology) and a better design using domain methods. I tested the UI elements briefly. I did not go into test driving styling, colours or positions. In Swing components have all these properties accessible, e.g. visibility, colours, positions and more. So I could have asserted them. That is nice because I could go as far as I wanted to. I am undecided if I should test colours and other styling related things. As I said, there seems to be consensus to not assert on layout or styling and I did not. In general it is still unclear (for me) how much automated testing is needed for an UI in such situations. While I did not write tests for everything, I already have a feeling that there are "lots of test but no application".

20 January 2020

Login Form TDD a UI Kata

User interfaces are usually considered hard to test, and people rarely develop their user interfaces using Test Driven Development. So it is considered hard. I like hard and I like challenges. I started to research the topic last year. Actually I went crazy. Besides my own experiments and practice, I hijacked most Coding Dojo and Coderetreat sessions and tried to TDD user interfaces with my pairing partners. I discussed the topic at unconferences and spent dedicated time with people in learning workshops. Besides its use I just wanted to see how far I could go.

This is the first article in a series describing what I learned. I spent more than half a year of my learning time on this topic and tried different approaches. In the past I found myself a different topic to research each year. Previous topics included Scheme and Architectural Refactoring. Currently I am investigating Splitting the Monolith. This makes me leave my research kind of unfinished - probably it is impossible to "finish" learning at all. I want to collect what I have learned during last half year before diving into something else.

The Exercise
To experiment with Test Driven Development of user interfaces, we need some UI to build. What would be a small UI that most people know? A login dialogue. I created a repository to drive the UI of a Login Form using TDD, the Login Form TDD UI kata. Your task is to create a Login window or form or web page. Here are the requirements:

Existing Code Back End
The exercise is focused on the front end part. Let's just assume an Authentication service, facade or end point which will be simulated in the tests. It has a method authenticate() to authenticate a user based on her phone, email or user name and her password. The call returns an AuthenticationResult which indicates success and an optional message for error situations. From now on the combination of a user's phone, email or user name is called the user's lookup. Under certain conditions, the UI logic will invoke this service or back end. Calls to the back end might take some time and/or block, so these calls must done asynchronously to keep the user interface responsive.

Requirements for the Minimum Functionality
  • There is a user name input field, which is limited to 20 characters.
  • The label "Phone, email or username" is left, next to the input field.
  • There is a password field, which is limited to 20 characters.
  • The password is either visible as asterisk or bullet signs.
  • The label "Password" is left, next to the input field.
  • There is a "Log in" button in the bottom right corner of the window.
  • There is a label in a red box above the button. It is only visible if there was an error.
These requirements are just describing the UI. The more interesting part is the logic. The logic uses the Authentication back end described above.
  • When user name and password are given, button "Log in" is clicked and the back end reports success, then the form is closed.
  • When user name and password given, button "Log in" is clicked but the back end reports an error, a message in the error line is shown and the form stays open.
  • While the back end is working, the "Log in" button is disabled.
My friend Thomas said that UI requirements need wireframes. So here is a sketch. Pretty, isn't it? ;-)

Bare Login Form Sketch
More Requirements
The goal of the kata is to experiment with driving the UI using tests. Maybe you want to ignore the styling (most people do) or ignore the visual elements completely. Or maybe you want to focus specifically on styling. Clearly we need more requirements like
  • More functionality while the back end is working
  • More logic in the view itself when username or password is not given
  • More UI elements like titles and logos
  • Detailed styling of all elements
  • Focus and tab order
  • Checkbox to show password
  • Caps Lock Warning
All these requirements and even more are listed in detail in LoginDialogRequirements.md inside the kata repository. The task is progressive: If you want more logic then go for more logic, for more styling add more styling. Thanks to Nick Babich, Software Testing Help and Anton Angelov this will be the most complete login you will ever build. Here is the styled sketch of the final Login:

Login Form Sketch of Everything Styled
Drive the UI of a Login Form using TDD
I created this kata to explore test driving UIs: login-form-tdd-ui-kata. Currently it contains code to get started in the following languages and UI frameworks:
  • Java/Swing
  • Java/Vaadin
  • Kotlin/Android
  • Go/raylib-go
  • JavaScript/plain browser DOM
  • JavaScript/React
Give it a try! If you do, please commit after each TDD step and share your repository so I can analyse it. Have fun!

19 August 2019

Y U NO TDD

Y U No TDDDuring this year's GeeCON the crew organised an Open Space evening. (An Open Space is a self-organising meeting where the agenda is created by the people attending.) I participated and ran a session on the question why we are not doing Test Driven Development. (Y U No Do TDD?) I am running TDD trainings from time to time and wanted to get more insight where people are stuck with TDD.

Context
As I said, an Open Space is self organising, and only people interested in TDD attended my session. This is a typical problem of communities of practice - only people interested in the topic attend - which results in us living in a bubble. For example long time TDD practitioner Thomas Sundberg and Shirish Padalkar, lead consultant at ThoughtWorks, participated in the discussion. Depending on the background of each individual participant, my original question was understood as:
  • Why are you not doing TDD on production work at all?
  • Why are you not doing TDD most of the time?
  • Why are you not doing TDD all the time?
I collected the reasons not to do TDD during the session which I want to share here. Text inside quotation marks, e.g. "hi" quotes exactly what people said. While the previous three questions are slightly different, the reasons seem to be similar. I grouped the answers. I did not want to contradict or debunk these answers and have to hold back not to do so ;-)

Prototyping
I am "experimenting with something", the "expected outcome is unclear" and "it's only a prototype". Obviously these are valid reasons as Spikes are outside of TDD. These answers usually coming up quickly makes me wonder if they are kind of excuses sometimes. Experimenting with new libraries and APIs is covered further down, so what are we experimenting with? I worked with many developers who would agree that the expected outcome of their current ticket was unclear - because they did not take the time to analyse the story and understand the solution they were supposed to build? Additionally most of our prototypes go to production after all, don't they ;-)

Time Pressure
Another reason - given by some of my clients too - is their need to go fast: "I need to go very fast", there is "no time for that" and I "believe to be faster without it". While they might be wrong in the long term I understand the effects of pressure. One person made it more explicit, while he has no strong deadline, he said "I have a huge backlog, I am stressed". Indeed when I am extremely stressed, I find it hard to maintain a structured approach, especially if a lot of task switching is involved. Besides the needed skill to apply TDD under high load, much discipline is required to endure pressure. In such situations Strong Opinions and Dogma might help.

Missing the Bigger Picture
I am just "writing a script for myself". Maybe there is no need for automated tests when writing a one time script for myself. TDD has a testing aspect - and it has many other aspects like designing software, fast feedback and working incrementally. TDD is not only about testing. Some people miss that or have only partial understanding of the benefits or do not care for these benefits at the moment. The opposite reason is "because I know how the class will look like". Yes TDD is about software design as I said before, and I would like my class to work, too. Some people only want the fast feedback, e.g. using REPL based development and "looking at UI is faster".

Missing Priority on Testing
When starting with TDD, the testing aspect is most visible. After all we have to write a reasonable test first. For teams and organisations with low or missing priority on testing, people are "looking down on testing" and I got answers like "testing is a culture thing", "testing is not a first class activity" and "I am not asked to create a test by my project manager". Indeed it is hard to keep following TDD if it is looked down upon and if there is no time for quality work.

Avoiding Context Switches
There is a certain amount of context switching involved in TDD. Similar to Edward de Bono's Six Thinking Hats, we have different states which we have to be mindful of and which call for different actions. George Dinwiddie created a TDD Hat to show that. Maybe this switching is "not natural for some people". "I don't want to interrupt creative design with verification" and "I prefer staying in building hat and not change to testing hat". Similar one participant said that it is "easy to write code, harder to write tests, so I do it afterwards". I understand and there is certainly an urge to jump into the code and get hacking. I rarely feel that urge and I enjoy pair programming using the Ping Pong style because it enforces the separation of states without any (inner) discussion.

Missing TDD Skills
This is obviously the largest area and there is nothing wrong with not knowing how to apply Test Driven Development: Honest people just say "I can't do it". Many are aware of this problem and seem to be disappointed with existing material and/or look for more material to study TDD: "It is not taught at universities", "there are no good books" and "I am missing real examples". I know from my own experience that TDD is not easy to learn and some people are "scared for life after a bad experience" with it. Now the best way to learn TDD is to have someone show you while pairing with you. Even if there is no pair programming in your workplace, you can still experience it during a Coding Dojo or Coderetreat. Short of that, I recommend Kent's Beck Test Driven Development by Example, which is a short and excellent introduction.

New Language or Library
When discussing TDD and unit testing with a client, he said I "don't know the target technology" and "React is a new technology for us". I had to laugh. To me this sounded like "I got a car and know how to drive forward, but am not able to drive backwards." On the other hand I live at the dead end of a road and I see drivers working really hard to avoid driving backwards. Are they not able to do it? So maybe stopping halfway in the game (of skill acquisition) is natural after all. When working with some new language or working with an unknown API, I specially rely on tests to support me, these are Learning Tests.

It's too hard to test
I agree some things are harder to test than others. "Android is hard to test", "Vaadin is hard to test" and "some libraries are hard to test". (I have not worked with Android or Vaadin, I quote people.) We might need to know more about design to decouple things. This is definitely true for legacy code, as "existing code is usually hard to test". Some people see the root cause, like in "I don't know how to manage boundaries". In such situations we need (to know) more tooling. We definitely "need more tooling to test the UI" as UI is traditionally considered hard to test from a TDD perspective. Still, Steve Freeman and Nat Pryce, authors of Growing Object-Oriented Software Guided by Tests, always start their TDD (outer) loop with an UI test. GOOS is a great book and I recommend reading it if you want to go deeper into TDD.

It's too simple to test
If there are things which are too hard to test, there must also be things which are too simple to test, right? It is "useless to test, it is so simple" and it "makes no sense to test it". Maybe a better description is that it is "unclear what is important to test". From a TDD perspective no such things exist and I guess these reasons arise from the test after process, when looking at each public method and thinking how to test it. Further excessive test isolation, see Solitary vs. Sociable Unit Tests, will cause that.

Barriers to TDD adoption
Here is Matt Wynne's summary of Barriers to TDD adoption from a session during Lean Agile Scotland 2016. I recommend checking out the Twitter thread as Matt added detail discussions on temptation of fast reward, permission and safety to learn, "the egotist" and other reasons not covered by me.

Barriers to TDD adoption #lascot16 (C) Matt Wynne
What about test-induced design damage?
Maybe the only real reason not to do TDD is to keep the design integrity of your system. This idea was started back in 2014 by David Heinemeier Hansson, also known as DHH, and led to the whole Is TDD Dead? debate. DHH said that when using TDD code sometimes suffers tremendous design damage to achieve two testing goals: Faster tests and easy-to-mock unit tested controllers and that the design integrity of the system is far more important than being able to test it any particular layer. It is ironic that this never comes up during any group discussion or team interview. Probably because it is an expert level reason. If you followed the debate, DHH knew TDD, he used it for some time and liked it. And then, only then, did he know when not to apply it.

6 November 2015

Bank-OCR Kata in Scheme

Why is it when I wanted to do something completely different from work to relax - I end doing code katas in Scheme? (Last week on Twitter.)

SoCraTes Belgium
Two weeks ago I attended SoCraTes Belgium, the Belgian branch of the Software Craftsmanship and Testing family of (un-)conferences. Being an un-conference the complete agenda of both days was created by the participants. Early during the first day, a participant proposed a session to work through the exercises of the well known SICP book using the Scheme programming language. We worked through the exercises as a mob and it was a lot of fun so another session was scheduled for afternoon. Time and again Scheme sessions were scheduled outside the official agenda. In the end I had spent several hours playing with Scheme and I really enjoyed it.

Why Scheme?
Later I started doing code katas in Scheme, which surprised me, see my initial quote. I do not know why I chose Scheme. There were other programming languages I had planned to learn. Maybe, as my friend Thomas remarked, I chose Scheme because it is seldom used in commercial projects, at least in my surroundings. As it is far away from anything I might touch during my regular work, it is easier to have fun with.

Unit Testing
Nevertheless I wanted to follow my typical development process, using TDD and looked for recommended unit testing frameworks. As minimalism is in the spirit of Scheme, I went with the Scheme Unit outlined on Cunningham's Wiki,
(define (report-error msg)
    (error (string-append "AssertionError: " msg)))

(define (assert msg b)
    (if (not b)
        (report-error msg)))
and added assertions whenever I needed them.

Banking the LightBank OCR
Today I want to share my take on the Bank OCR Kata using Scheme. The Bank OCR assignment is to parse files containing lists of numbers written in LCD style using only pipes and underscores. Each number has nine digits, all of which are in the range one to nine. I knew the Bank OCR kata and had done it before using different languages like Java or C#. I was familiar with the domain which allowed me to focus on functional programming in Scheme.

Outside-In
In the past I used the Bank OCR kata especially to practise the outside-in way of development. Using this approach you build the system from the "outside-in", following the user interaction through all the parts of the system. You start with the interactions and collaborators up-front, especially those at the top level and create fake implementations or mock necessary dependencies. With every finished component, you move to the previously mocked collaborators and implement them. See Emily Bache's article on Outside-In development for a discussion of Outside-In both with London school and classic TDD.

Outside-In vs. Functional?
So whenever I did the Bank OCR kata I tried to follow strict outside-in. But this time I wondered if the outside-in approach was feasible when using a functional language? As far as I knew the typical way of functional programming was to compose small functions to more powerful ones, which naturally lent itself to the bottom-up or classic approach. I was curious how these two would match, if at all.

The Guiding Test
Following Double Loop TDD I started with failing guiding test to parse a single number containing all possible digits,
(define all-digits (list "    _  _     _  _  _  _  _ "
                         "  | _| _||_||_ |_   ||_||_|"
                         "  ||_  _|  | _||_|  ||_| _|"
                         "                           "))

(assert-list= string=?
              "should parse a single number"
              (list "123456789")
              (bank-ocr all-digits))
which expected that bank-ocr(all-digits) yielded ["123456789"].

How to solve the problem?
Then I started to TDD the top level function bank-ocr.
(assert-list= string=?
              "should return empty list on empty input"
              (list)
              (bank-ocr (list)))
which created the initial function. Then I tested for a non-trivial case
(assert-list= string=?
              "not sure about the name of the test yet"
              (list "123456789")
              (bank-ocr all-digits))
Big Ben (London School TDD is Outside-In but Classic TDD can be as well.)But how would I solve the problem? I had no idea. Nevertheless, the first step of the algorithm was to split the input into groups of four lines each and another function, e.g. parse-line, would parse the line then. Following outside-in I defined a stub for parse-line and changed bank-ocr to call it.
;; stub
(define (parse-line ocr-line)
    "123456789")

(define (bank-ocr ocr-lines)
    (if (null? ocr-lines)
        '()
        (list (parse-line ocr-lines))))
The next test forced me to implement the recursion to call parse-line for each group of four lines.
(assert-list= string=?
              "should parse each group of lines"
              (list "123456789" "123456789")
              (bank-ocr (append all-digits all-digits)))

(define (bank-ocr ocr-lines)
    (if (null? ocr-lines)
        '()
        (append (list (parse-line (take ocr-lines 3)))
                (bank-ocr (drop ocr-lines 4)))))
Moving "in"
bank-ocr was complete but the guiding test told me that there was no parse-line function in the production code and I knew where to go next.
(define ocr-digit-one (list "   "
                            "  |"
                            "  |"))

;; should split and parse first digit
(assert-string= "1" (parse-line ocr-digit-one))
Parsing a line would need to split the line into digits and then parse each digit. I added another two stubbed functions and built parse-line to get the test green.
;; stub
(define (split-digits ocr-line)
    ocr-digit-one)

;; stub
(define (parse-digits ocr-digits)
    ;; use assert-list= to check that ocr-digits is ocr-digit-one
    "1")

(define (parse-line ocr-line)
    (parse-digits (split-digits ocr-line)))
Parenthesis(Actually I was cheating here: I should have checked that the output of split-digits was fed into parse-digits. Nobody is perfect and I will atone for that later, but let's move on for now.) Again a function was finished but I had invented two new collaborating functions to do so.

Another step "outside-in"
Next came testing split-digits to split the three lines into nine digits containing three lines of three characters each.
(define two-ocr-digit-one (list "      "
                                "  |  |"
                                "  |  |"))

;; missing test "should split empty line into no digits"

(assert-list= (list-equals-for string=?)
              "should split single digit"
              (list ocr-digit-one)
              (split-digits ocr-digit-one))

(assert-list= (list-equals-for string=?)
              "should split two digits"
              (list ocr-digit-one ocr-digit-one)
              (split-digits two-ocr-digit-one))

(define (split-digits ocr-line)
    (define (take-3-chars s)
        (substring s 0 3))
    (define (drop-3-chars s)
        (substring s 3 (string-length s)))
    (if (zero? (string-length(car ocr-line)))
        '()
        (append (list (map take-3-chars ocr-line))
                (split-digits (map drop-3-chars ocr-line)))))
I did not start with the degenerate test-case that an empty line, a list of three empty strings, should be split into an empty list of digits. I did not add this test because it did not feel right from the solution's perspective. split-digits would always be called with a full line, i.e. three strings of 27 characters each. But as soon as I tried to get the recursion for the second digit right (as forced by the second test), I struggled because I had to figure out the recursion and termination condition at the same time.

ParenthesisA Functional TDD "Pattern"
There is some obvious pattern here. Consider we need a function that operates on a list of inputs and processing of a single input is either simple or can be delegated to another function. Then we need three tests to drive the implementation of that function:
  1. An empty input should produce an empty output, where empty is defined differently for input and output. This drives the creation of the function header and the body of the (future) termination condition.
  2. A single input should produce a single output. This drives the conditional for the termination condition and the processing of a single input. The processing must be simple otherwise the step is too large.
  3. A list of inputs should produce a list of outputs. This test drives the splitting of the first input from the remaining ones for the recursion.

Coming to an end
The second missing function was parse-digits. It was supposed to work on a list of digits, to parse each of them and return the list of parsed digits so I used my three steps from above.
;; should parse empty digits as empty string
(assert-string= "" (parse-digits (list)))

;; stub
(define (parse-digit ocr-digit)
    "1")

;; test for parsing a single digit omitted

;; should parse digits into numbers for each digit
(assert-string= "111"
                (parse-digits (list ocr-digit-one ocr-digit-one ocr-digit-one)))

(define (parse-digits ocr-digits)
    (if (null? ocr-digits)
        ""
        (string-append (parse-digit (car ocr-digits))
                       (parse-digits (cdr ocr-digits)))))
I skipped step two of my list above and omitted the test for parsing a single digit because I felt confident and delegated the actual parsing of a single digit to another function. parse-digit was the final function and compared a given digit against stored digits to determine the number.
(assert-string= "should parse one"
                (parse-digit ocr-digit-one))

(assert-string= "should parse two"
                (parse-digit ocr-digit-two))

;; etc.

(define (parse-digit ocr-digit)
    (let ((digit (apply string-append ocr-digit)))
        (cond ((string=? digit (string-append "   "
                                              "  |"
                                              "  |")) "1")
        ;; etc.
    )
  )
)
I did not push the final solution of parse-digit. Probably I could remove the duplication using some functional magic, but I had spent some hours already on coding, it was late and I was tired. The full source is available here.

Conclusion
Using Scheme was fun. I had to look up library functions a lot and spent some time on Stackoverflow, but I felt progress all the time. I committed on red, green and refactor, on average each ten minutes, and I was never stuck. The minimalist unit testing function gave me enough feedback to move forward quickly. I did not bother for expressive assertion messages because my steps were small and I never looked at the failures anyway - I knew which test would fail or pass next. Solving Bank OCR was straight forward, probably due to the nature of the assignment. Also knowing the solution - which is not the implementation - helped me a lot and I focused almost entirely on Scheme and the functional aspect.

I was able to do outside-in TDD by stubbing future functions. The stubbing was crude, I just redefined the functions in the test code. I was unhappy with this approach but it worked and I lacked in-depth knowledge of Scheme to come up with a proper way to stub functions. It seemed wrong to pass functions around according to Dependency Inversion Principle, because the called functions were low-level internals and no peer collaborators. In a way I followed Ralf Westphal's approach of True Stepwise Refinement, where he stubbed private functions. In the end I thought about deleting (some) unit tests of the internal functions but did not have any conclusive ideas how to do so.

P.S. for Claus
We had agreed to code together and do some serious product development, and again I spent time on weird ideas like Scheme or Assembly. I am sorry.

18 June 2013

TDD Exam Questions

Last year I ran a three day in-house training on unit-testing and TDD using Eclipse. I wanted the course to be as interactive as possible to keep the attendees engaged all the time. Besides my slides I carried out exercises like Jason Gorman's TDD from Hell and played short movies to keep the content varied. At the end of each chapter I showed a slide with a few questions about the topic just covered. These questions gave the audience a break to recall the content just studied and reflect on it before moving on. I searched for such questions but did not find any, so I had to come up with my own.

These are my sanity-check questions. They are not complicated or long-winded, just simple questions to verify if the content was understood. Some questions also served as a base for further discussion. The developers in the audience were exposed to TDD for the very first time, so I kept them simple. If you are a seasoned developer I am sure these questions will not challenge you at all.

Writing ExamsUnit Testing with JUnit
  • What is a test fixture?
  • Which assert is used to compare the values of objects?
  • What about assertTrue(true);?
  • What about assertTrue(a.equals(b));?
  • How to test for an expected exception?
  • What about System.out.println(...); in tests?
  • How to data-drive tests?
  • How to time-out tests?
  • How to test the contents of a private field? (This is a trap, the answer is not to test it but to change the design.)
  • Does full code coverage mean the code is fully tested?
Test Driven Development
  • What are the benefits of using TDD?
  • Is TDD primarily a testing technique?
  • When do you write tests? (This is a joke and the expected answer is "all the f*cking time" according to Bryan Liles' TATFT.)
  • What should you do when you cannot add another test?
  • If the code passes all the unit tests why should you still change (refactor) it?
  • Why not refactor on a red bar?
Mocking and Test Doubles
  • Why use stubs or mocks?
  • How are objects called that are passed around but never actually used?
  • How are objects called that can return fixed values in answer to calls made.
  • How are objects called that can verify the behaviour of the system under test, in addition to using asserts to verify its state.
  • What is used for testing state?
  • What is used for testing behaviour?
  • What is a spy?
ExamRefactoring
  • What is your favourite Refactoring?
  • How do we refactor? (This is a word play and the expected answer is mercilessly.)
  • Should Refactoring and adding new code be done at the same time?
  • During Refactoring, how long does your code not compile?
  • During Refactoring, when should we run the tests?
  • What Refactoring can be used when a method is too long, has duplicated code or Feature Envy?
  • What Refactoring can be applied to long parameter lists?
  • In Eclipse, what do the short-cuts Alt-Shift-R, Alt-Shift-L and Alt-Shift-M do?
Working with Legacy Code
  • What is Legacy Code?
  • How can you break dependencies?
  • What are Singletons and why are they evil?
  • Do you write them?
Using these questions, small exercises and short movies I kept the participants fully engaged for a whole day of TDD theory. It was awesome!

27 November 2012

See the new test fail

The Wikipedia article about Test-driven development describes the process of TDD. It says that after adding a new test, you should run all tests and see if the new one fails. This is part of the red-green-refactor cycle. During my remote pairing activities with Thomas I noticed that I tend to forget this step. To fix this I wrote a tiny Eclipse plugin that complains if a test run does not fail after adding a new test.

Alert! New Test Did Not Fail
The plugin attaches itself to the org.eclipse.jdt.junit.testRunListeners extension point and records the names of all tests during JUnit test execution. (See RedGreenListener.java) When the test session is finished, it compares these test statistics against the previous run. In fact the only thing we need to know is if any test cases have been added. (See TestStats.java) Based on the comparison the plugin decides if to show an annoying popup or not. (See TestRunDiff.java)

The current mechanism is simple and likely to be wrong for special cases:
public boolean firstTestOk() {
    return newAdded.size() == 0 || secondFailed;
}
but it works great on katas and small hobby projects.

The org.eclipse.jdt.junit.testRunListeners extension point is available in Eclipse Europa (version 3.3), but it does not work there. The schema testRunListeners.exsd for the extension point seems to be missing from the JDT/JUnit bundles. The situation changes in Helios (3.6) and JUnit starts to notify the declared listeners. As the current release of Eclipse is version 3.8/4.2, I believe that two versions of backwards-compatibility should be enough.

Download the plugin here. Copy it into your plugins folder and restart Eclipse.

23 November 2012

BaDaDam Testing Framework

DIY Cable ReleaseSome time ago I had a look into BDD - no, not Bullshit Driven Development ;-) - but Behaviour-Driven Development. I started with JBehave but it seemed heavyweight to me. Further it had more than ten dependencies to other libraries, some with possibly problematic licences. Still I wanted to know how that kind of framework worked, so instead of learning by taking it apart I decided to build my own.

For some time I struggled to find a nice name for the project and discussed my problem with Michael. I was mumbling as usual and he mistook BDD as BaDaDam. Voila - I present to you BaDaDam, a minimalistic BDD framework for Java. Obviously it follows the spirit of JBehave, is lightweight, self sufficient and depends only on JUnit. It allows you to write stories in plain text, implement them in Java classes and run them using JUnit. The first version is finished since some time and available in my Maven repository here.