Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing Winforms UI

I am writing a test case for my User Control which will prompt using MessageBox.Show for User Action asking to process or Cancel the operation. How can I design my unit test to mimic the User interaction to proceed?.

I do not want to refactor to move the logic to middle tier. This is a simple case of getting User Consent and proceeding with middle tier call. Any help/ideas restructuring UI for this scenario will also be helpful.

like image 660
SKG Avatar asked Dec 15 '09 20:12

SKG


2 Answers

Clicking a button is nothing else than invoking the corresponding click event. So you might want to build your test around that.

Even better (if this isn't the case yet), move your code out of the frontend, and build your unittests around the business actions, you'd otherwise invoke by clicking a button.

update after edit by author
You are not going to get this to work as long as you are not prepared to split things, you cannot build your unit tests around 'click here', 'click there'. Imagine the following code:

private int MyFunction()
{
    bool insideVariable = false;
    if(insideVariable) 
        return 1;
    else
        return 2;
}

You will never be able to unit test the case where insideVariable is set to true; You can either:

  1. Refactor your code so the return 1 statement is somewhere in your middle tier
  2. Refactor so that the return 1 statement is a method in your GUI. You can then test that function.

Application frontends should be quite easily to replace, so no business logic should be stored in there. Unit tests are just another frontend living next to your main GUI.

like image 198
Jan Jongboom Avatar answered Sep 27 '22 23:09

Jan Jongboom


Providing a solution would be much easier with the UI method or related methods posted. Also seeing the TestMethod(s) could help even incomplete methods.

If I understand your test purpose is to determine what happens on the different click possibilities?

You could set up your actual method that triggers the MessageBox using Inversion of Control and Dependency Injection like this:

public class ClassUnderTest
{
    private static Func<string, string, MessageBoxButtons, DialogResult>
       _messageBoxLocator = MessageBox.Show;
    public static Func<string, string, MessageBoxButtons, DialogResult> 
       MessageBoxDependency
    {
        get { return _messageBoxLocator; }
        set { _messageBoxLocator = value; }
    } 

    private void MyMethodOld(object sender, EventArgs e)
    {
        if (MessageBox.Show("test", "", MessageBoxButtons.YesNo) == 
            System.Windows.Forms.DialogResult.Yes)
        {
            //Yes code
            AnsweredYes = true;
        }
        else
        {
            //No code

        }
    }

    public bool AnsweredYes = false;

    public void MyMethod(object sender, EventArgs e)
    {
        if (MessageBoxDependency(
                    "testText", "testCaption", MessageBoxButtons.YesNo) 
            ==
            System.Windows.Forms.DialogResult.Yes)
        {
            //proceed code
            AnsweredYes = true;
        }
        else
        {
            //abort code
        }


    }
}

and then the test method (remember to include the using Microsoft.VisualStudio.TestTools.UnitTesting; at the top) would be like this:

[TestMethod]
    public void ClassUnderTest_DefaultAnsweredYes_IsFalse()
    {
        var classUnderTest = new ClassUnderTest();
        Assert.AreEqual(false, classUnderTest.AnsweredYes);
    }
    [TestMethod]
    public void MyMethod_UserAnswersYes_AnsweredYesIsTrue()
    {
        //Test Setup
        Func<string, string, MessageBoxButtons, DialogResult> 
            fakeMessageBoxfunction =
                    (text, caption, buttons) =>
                    DialogResult.Yes;

        //Create an instance of the class you are testing
        var classUnderTest = new Testing.ClassUnderTest();
        var oldDependency = Testing.ClassUnderTest.MessageBoxDependency;
        Testing.ClassUnderTest.MessageBoxDependency = fakeMessageBoxfunction;
        try
        {
            classUnderTest.MyMethod(null, null);
            Assert.AreEqual(true, classUnderTest.AnsweredYes);
            //Assert What are you trying to test?
        }
        finally
        { //Ensure that future tests are in the default state
            Testing.ClassUnderTest.MessageBoxDependency = oldDependency;
        }
    }
like image 37
Maslow Avatar answered Sep 27 '22 22:09

Maslow