Organising unit-test data

How do you organise the data for your unit tests? In most non-trivial applications you will have a fairly complex domain model. Again and again you will find yourself needing to create instances of such complex objects for your tests.
Over the time I’ve used 3 methods for this:

  1. Copy-Paste. It’s quick, and being unit-tests it’s not such a no-go. It also keeps tests independent of one another (as opposed to method 2). The downside is when you need to update the test data, for example if you add a new field – you’ll need to go over all the copies and update them.
  2. TestDataHelper. Have a class with static methods that create and pre-fill your objects with data. For any updates you only need to change one place. The downside is that it couples your tests between them. If you need different data for one test, and you change your Helper class, it may break other tests.
  3. TestBuilders. This is my favourite method right now. You create builder classes for your complex objects, with parametrised methods for child objects. This way you have one central place to make changes, but your tests are independent, as they don’t share data.
    Here’s an example:

    Catalog catalog = new CatalogBuilder()
    	.addProduct("Chair", "Wood")
    	.withCategory("Furniture")
    	.withCategory("Kitchen")
    	.addProduct("Sofa", "Leather")
    	.withCategory("Furniture")
    	.withCategory("Living Room")
    	.build();
    

Which method do you use?

Windows RTF Editor with Swing

Ever tried using an RTF editor component for Java/Swing? I did, and I was never satisfied with the ones I found. Neither were the users. One of the reasons was that pretty much all of the users were working on Windows and had become accustomed to the native RTF Editor component from the application we were replacing.
So I thought, why not use the Windows component from the Swing application? How hard can it be?
As a note, I’ve only tried this in a private test project, what you’re about to see is no production code.

The Java side is quite succint:

public class RTFEditor {

  {
    System.load("c:\\myRTFEditor\\rtf.dll");
  }

  private static native String showRTFEditor();
}

There’s just the native method that will display a modal dialog with the RTF component and will return the text that the user entered.

Getting to the native stuff (I used VC++ 10 with Visual Studio)

The header file, rtf.h, generated with javah.exe

/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_me_RTFEditor */

#ifndef _Included_com_me_RTFEditor
#define _Included_com_me_RTFEditor
#ifdef __cplusplus
extern "C" {
#endif

namespace rtf {

  JNIEXPORT jstring JNICALL Java_com_me_RTFEditor_showRTFEditor(JNIEnv *, jclass);
}

#ifdef __cplusplus
}
#endif
#endif

And the implementation, rtf.cpp. I got the string converstion code from Stackoverflow, unfortunately I don’t remember exactly from where.

#include "stdafx.h"
#include "rtf.h"
#include "rtfForm.h"
#include

using namespace std;
using namespace System::Text;
using namespace System::Runtime::InteropServices;

namespace rtf {

  JNIEXPORT jstring JNICALL Java_com_me_RTFEditor_showRTFEditor(JNIEnv * env, jclass)
  {
    rtfForm^ form = gcnew rtfForm();
    //show modal dialog
    form->ShowDialog();
    // Encode the rtf text from the component as UTF8
    array^ encodedBytes = Encoding::UTF8->GetBytes(form->getRtf());

    // prevent GC moving the bytes around while this variable is on the stack
    pin_ptr pinnedBytes = &encodedBytes[0];

    // convert to jstring to return it to java
    jstring jstr2 = env->NewStringUTF(reinterpret_cast<char*>(pinnedBytes));
    return jstr2;
  }
}

You surely noticed a “rtfForm” is referenced here. I used the UI Designer for it, it’s simply a form with a RTF Editor component inside. Here’s the code for it (rtfForm.h).
I won’t include the code for rtfForm.cpp, as it contains just the include for rtfForm.h

#pragma once

namespace rtf {

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;

///
/// Summary for rtfForm
///

public ref class rtfForm : public System::Windows::Forms::Form
{
  public:
  rtfForm(void)
  {
    InitializeComponent();
  }

  String^ getRtf()
  {
    return this->richTextBox->Text;
 }

  protected:

  ~rtfForm()
  {
    if (components)
    {
      delete components;
    }
  }
  private: System::Windows::Forms::RichTextBox^ richTextBox;
  protected:

  protected:

  private:
  ///
  /// Required designer variable.
  ///

  System::ComponentModel::Container ^components;

  #pragma region Windows Form Designer generated code
  ///
  /// Required method for Designer support - do not modify
  /// the contents of this method with the code editor.
  ///

  void InitializeComponent(void)
  {
    this->richTextBox = (gcnew System::Windows::Forms::RichTextBox());
    this->SuspendLayout();
    //
    // richTextBox
    //
    this->richTextBox->AcceptsTab = true;
    this->richTextBox->Anchor = static_cast((((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Bottom)
    | System::Windows::Forms::AnchorStyles::Left)
    | System::Windows::Forms::AnchorStyles::Right));
    this->richTextBox->Font = (gcnew System::Drawing::Font(L"Courier New", 8.25F, System::Drawing::FontStyle::Regular,     System::Drawing::GraphicsUnit::Point,
static_cast(0)));
    this->richTextBox->Location = System::Drawing::Point(1, 3);
    this->richTextBox->Name = L"richTextBox";
    this->richTextBox->Size = System::Drawing::Size(477, 217);
    this->richTextBox->TabIndex = 0;
    this->richTextBox->Text = L"";
    //
    // reForm
    //
    this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
    this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    this->AutoScroll = true;
    this->ClientSize = System::Drawing::Size(479, 221);
    this->Controls->Add(this->richTextBox);
    this->Name = L"reForm";
    this->Text = L"reForm";
    this->ResumeLayout(false);

    }
  #pragma endregion
  };
}

And here’s a screenshot of it working
rtf