views:

300

answers:

3

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.

+3  A: 

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.

Jan Jongboom
I can use SendKeys.SendWait similar stuff, but the problem here is ShowDialog is a blocking call
SKG
You shouldn't try to do really clicking the button, but just something like `new Form1().Button1_Click(this, null)`.
Jan Jongboom
+1 - If the UI holds enough logic that duplicating its action requires actually using the UI code, then the UI contains too much logic. It might be worth noting exceptions - presentation logic is always tricky in this regard - but in general testable code doesn't live in the UI to begin with.
Mike Burton
@Jan. I have edited my question. Basically I am using a simple MessageBox.Show (not a Form.ShowDisalog as I had mistakenly stated before). This is the blocking code. I don't think Form1().Button1_Click(this, null) will work here.
SKG
@SKG See my edit
Jan Jongboom
+1  A: 

I agree with Jan. Move your code out of the UI to a more easily tested class, and then smoke test your UI with a UI automation tool, with the business logic tested in the unit tests.

Tony Eichelberger
If you agree, just upvote the answer that was already there :-)
Jan Jongboom
+1  A: 

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?

your actual method that triggers the MessageBox would have something 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
        }


    }
}

then the tests(using Microsoft.VisualStudio.TestTools.UnitTesting;) 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;
        }


    }
Maslow