Winforms UI的单元测试

6
我正在为我的用户控件编写测试用例,该控件将使用MessageBox.Show进行提示,要求用户执行或取消操作。 我如何设计单元测试来模拟用户交互以进行进一步操作? 我不想重构将逻辑移动到中间层。 这只是获取用户同意并继续进行中间层调用的简单情况。 对于此场景重新构建UI的任何帮助/想法也将很有帮助。

所以你想测试一下当用户点击它们中的每一个时,你的控件会做什么?或者测试本身有一个阻塞消息框对话框来查看中间层是否安全进行测试? - Maslow
3个回答

7
点击按钮无非是调用相应的click事件。因此,您可能希望围绕此构建测试。
更好的做法是(如果尚未这样做),将代码移出前端,并围绕业务操作构建单元测试,否则您将通过点击按钮来调用它们。
作者编辑后更新: 只要您不准备拆分事物,就不会使其正常工作。您不能围绕“点击这里”、“点击那里”构建单元测试。想象一下以下代码:
private int MyFunction()
{
    bool insideVariable = false;
    if(insideVariable) 
        return 1;
    else
        return 2;
}

你永远无法对设置insideVariable为true的情况进行单元测试;你可以选择以下两种方法:

  1. 重构你的代码,使return 1语句出现在你的中间层中的某个位置。
  2. 重构代码,使return 1语句成为GUI中的一个方法。然后你就可以测试该函数。

应用程序前端应该很容易替换,因此不应存储任何业务逻辑。单元测试只是与你的主GUI并存的另一个前端。


我可以使用SendKeys.SendWait等类似的方法,但问题在于ShowDialog是一个阻塞调用。 - SKG
你不应该尝试真正地点击按钮,而只需要像 new Form1().Button1_Click(this, null) 这样做。 - Jan Jongboom
2
如果UI包含足够的逻辑,以至于复制其操作需要实际使用UI代码,则UI包含过多的逻辑。值得注意的是,展示逻辑在这方面总是棘手的例外情况,但通常可测试的代码本来就不应该存在于UI中。 - Mike Burton
@Jan。我已经编辑了我的问题。基本上,我正在使用一个简单的MessageBox.Show(不是我之前错误地说的Form.ShowDisalog)。这是阻塞代码。我不认为Form1().Button1_Click(this, null)在这里起作用。 - SKG

4
提供 UI 方法或相关方法后,解决方案会更容易。此外,查看 TestMethod(s) 可以帮助即使是不完整的方法。
如果我理解得正确,您的测试目的是确定在不同的点击可能性下会发生什么?
您可以使用 控制反转依赖注入 来设置触发 MessageBox 的实际方法,如下所示:
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
        }


    }
}

然后测试方法(记得在顶部包含using Microsoft.VisualStudio.TestTools.UnitTesting;)应该像这样:

[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;
        }
    }

0

1
请将此作为注释。 - ckruczek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接