Take the 2-minute tour ×
Software Quality Assurance & Testing Stack Exchange is a question and answer site for software quality control experts, automation engineers, and software testers. It's 100% free, no registration required.

Alright, so this is what I have right now.

buttonMultiply_Click(object sender, EventArgs e){
    Calculator c = new Calculator();
    textBoxLog.Text = "Calculation underway...";

    int value1 = Convert.ToInt16(textBoxValue1.Text);
    int value2 = Convert.ToInt16(textBoxValue2.Text);
    int res = c.Multiply(value1, value2); //Sorry for asinine example
    textBoxResult.Text = res.ToString();
}

Ok, now to my understanding in the above code there is a clear separation between UI (form element manipulation) and business logic (Calculator).

If I were to do a business layer test, I would create my own inputs for Calculator.Multiply, and compare the result to a predefined result:

Unit Test (Business Logic)

[Test]
TestMultiply(){
    int value1 = 5;
    int value2 = 6;

    int correctValue = 30; 
    Calculator c = new Calculator();
    Assert.AreEqual(correctValue, c.Multiply(value1, value2));
}

Questions:

  • How would I unit test the UI? Like, in this simple example, how could I write a unit tests that ensures that the correct values from the UI are sent to my Calculator object?

  • In addition to unit testing the UI's connection between the form's controls and business layer, what else would require unit testing in this class?

THANKS!!!

share|improve this question
add comment

4 Answers 4

up vote 4 down vote accepted

Ok, this is going to be a long answer, sorry. You approach is nearly there, but we can test a lot more if we do a little more abstraction.

Firstly lets define an interface for your form (view) that will get the values that have been input and set the result value back.

public interface IView
{
    string Input1TextBox { get; }
    string Input2TextBox { get; }
    string ResultTextBox {set;}
}

Now lets create a presenter that will have all our calculation logic inside it. The constructor will take an implementation of IView that we have just defined. This will be use this to get the inputs and set the result.

public class MyPresenter
{
    private IView _view;

    public MyPresenter(IView view)
    {
        _view = view;
    }

    public void DoCalculation()
    {
        int value1 = Convert.ToInt16(_view.Input1TextBox);
        int value2 = Convert.ToInt16(_view.Input2TextBox);
        _view.ResultTextBox = (value1*value2).ToString();
    }
}

Now we can implement and form (view) that implements the IView interface. In the constructor for the form we create a new instance of MyPresenter so that we can access the DoCalculation method that we have written.

public partial class Form1 : Form, IView
{
    private MyPresenter _presenter;

    public Form1()
    {
        _presenter = new MyPresenter(this);
        InitializeComponent();
    }

    #region Implementation of IView

    public string Input1TextBox
    {
        get { return input1TextBox.Text; }
    }

    public string Input2TextBox
    {
        get { return input2TextBox.Text; }
    }

    public string ResultTextBox
    {
        set { resultTextBox.Text = value; }
    }

    #endregion

    private void button1_Click(object sender, EventArgs e)
    {
        _presenter.DoCalculation();
    }
}

Why is this good?

By doing this we have a UI layer that is ultra thin and a testable presenter that contains our business logic.

We can test our presenter with a mock implementation of IView and assert that the presenter correctly made calls to get input 1 and input 2, as well as assert that the presenter made the call to set the result on the mocked view.

We can also test that that presenter functions correctly by setting our mock object up to return values for input 1 and 2, and asserting the result set on the mocked view. This could be made even easier by abstract the calculation logic out further such as:

    public void DoCalculation()
    {
        int value1 = Convert.ToInt16(_view.Input1TextBox);
        int value2 = Convert.ToInt16(_view.Input2TextBox);
        _view.ResultTextBox = Calculator.Multiply(value1, value2);
    }

Here you could easily write unit test to make sure that the Multiply method works correctly and the presenter test would make sure that the correct calls were made on the view.

Essentially one fully integrated UI test is all that is needed to ensure that the correct inputs where pass through and the result was set in the correct text box. This would be enough to have confidence in that the UI logic was correct.

Hopefully this is of help to you.

share|improve this answer
    
Can you please explain how to write a fully integrated UI test? I put everything you described in your post into a new project, and when I create a new Form1, I can't set Value1, and Value2 in my code, or execute the calculate method. I'm missing the mark on this one, if you could give me a few hints, that would really help! :) –  sooprise Jun 7 '11 at 21:41
    
Also, +1, THIS POST IS STACK EXCHANGE GOLD!!!!!!!!!!! I've been reading about MVP today, and nothing on the internet comes close to how well this post illustrates the use of the MVP model. Thanks!!! –  sooprise Jun 7 '11 at 21:42
    
I can add set to Value1, Value2, and get to Result, and make calculate external to the button press and public, allowing me to do: Form1 f = new Form1(); f.Value1 = "4";f.Value2="5";f.Calculate();Assert.AreEqual(f.Result,"20");, is that the right approach? –  sooprise Jun 7 '11 at 21:54
add comment

Abstract out the call to multiply, and the setters to the other fields. so it looks like buttonMultiply_Click(object sender, EventArgs e){ doMultiply(); }

Then write the test in english:

  1. Create GUI
  2. Place 5 in box 1
  3. Place 6 in box 2
  4. Multiply
  5. Verify result

then turn into code ( i would use approvaltests for this)

var c = new Calculator();
c.SetBox1(5);
c.SetBox2(6);
c.Multiply();
Approvals.Approve(c);
share|improve this answer
add comment

Sorry for another lengthy explanation and an alternate approach via the Model - View - Controller pattern. This is another pattern that isolates the user interface (UI) from the business logic (e.g. the calculator class in your example).

Let's start with the model, or the calculator class. As in your example, that is where the business logic lives.

interface ICalculatorModel
{
    long Multiply(int mValue1, int mValue2);
}

class CalculatorModel : ICalculatorModel
{
    public long Multiply(int mValue1, int mValue2)
    {
        return Math.BigMul(mValue1, mValue2);
    }
}

This calculator model contains the business logic for multiplication of 2 integer values. (It returns a long value instead of an int to prevent an overflow error in case the product of 2 values would exceed 2,147,483,647.) The model can be extended to include methods for division, addition, subtraction, square root, etc., completely independent of the user interface. (Notice that the calculator class does not instantiate the controller or the view interfaces.)

The unit test for the multiply method in your calculator class is almost identical to the unit test you outlined, but again we can decouple unit testing the business functionality from the user interface completely.

    [TestMethod()]
    public void MultiplyTest()
    {
        CalculatorModel calcModel = new CalculatorModel(); 

        int value1 = 5; // Initialize to an appropriate value
        int value2 = 5; // Initialize to an appropriate value
        long expected = 25; // Initialize to an appropriate value
        long actual;
        actual = calcModel.Multiply(value1, value2);
        Assert.AreEqual(expected, actual);
        Assert.Inconclusive("Verify the correctness of this test method.");
    }

Also, with more extensive data driven unit tests, and/or API testing we could easily develop robust automated tests to test the functionality (business logic) of the method(s) in the calculator class.

Ideally, the business logic in the calculator class knows nothing about the user interface (view). Communication between the view (user interface form code) and the model (calculator class in this example) we can use a controller. Since the controller has to know about both the view and the model we instantiate both the view and the model interfaces in the controller class.

public interface ICalculatorController
{
    void OnMultiplyClick(int cValue1, int cValue2);
}

class CalculatorController : ICalculatorController
{
    ICalculatorModel calcModel;
    ICalculatorView calcView;

    public CalculatorController(ICalculatorModel calcModel, ICalculatorView calcView)
    {
        this.calcModel = calcModel;
        this.calcView = calcView;
        this.calcView.Listener(this);
    }

    public void OnMultiplyClick(int cValue1, int cValue2)
    {
        calcView.result = calcModel.Multiply(cValue1, cValue2).ToString();
    }
}

Finally, we have the form code (or view) similar to your design of 3 textboxes and a button for multiplying the values in the textboxes (assuming they are integer values).

interface ICalculatorView
{
    void Listener(ICalculatorController calcController);
    int value1 { get; }
    int value2 { get; }
    string result { set; }
}

public partial class formCalculatorView : Form, ICalculatorView
{
    ICalculatorController calcController;

    public formCalculatorView()
    {
        InitializeComponent();
    }

    public void Listener(ICalculatorController calcController)
    {
        this.calcController = calcController;
    }

    private void buttonMultiply_Click(object sender, EventArgs e)
    {
        calcController.OnMultiplyClick(value1, value2);
    }

    public int value1
    {
        get { return int.Parse(textBoxValue1.Text); }
    }

    public int value2
    {
        get { return int.Parse(textBoxValue2.Text); }
    }

    public string result
    {
        set { textBoxResult.Text = value; }
    }

}

Notice that the buttonMultiply_Click() method calls the method in the controller, and the controller sends the result back to the view (the result textbox on the form).

Now, that we have our view (or user interface code), there are additional tests that we would want to run from the user interface in order to test behavioral aspects of how a customer might interact with the UI. For example, we might include a small subset of valid entries to verify that the event handler is calling the right method in the controller. If we did a thorough job testing the calculator class we shouldn't necessarily need to do extensive 'functional' re-testing of the calculation methods. What we should focus on are behavioral testing such how does the program handle non-integer input values into the textboxes, or numbers larger than 2,147,483,647 in either textbox. (Notice that in this example there is no exception handling and in either of these cases the int.parse() methods would throw unhandled exceptions.) Other things to look for might include visual layout (look and feel) and accessibility issues.

Certainly we can test for business logic functionality through the user interface, and I suspect in many cases that how testing is commonly approached. But, I think this and Stuart's example of separating the business logic from the user interface provides an alternate viewpoint of how we can also think of different testing approaches, and help us understand benefits and limitations of testing at different layers.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.