C# 使用UI控件的单元测试方法

C# 使用UI控件的单元测试方法,c#,unit-testing,mocking,C#,Unit Testing,Mocking,我目前正在写一些方法,这些方法在表单控件上执行一些基本操作,例如Textbox、Groupbox,这些操作是通用的,可以在任何应用程序中使用 我开始编写一些单元测试,只是想知道我应该使用System.Windows.Forms中的真正表单控件,还是应该模拟我要测试的部分。例如: 假设我有一个接受控件的方法,如果它是一个文本框,它将清除text属性,如下所示: public static void clearall(this Control control) {

我目前正在写一些方法,这些方法在表单控件上执行一些基本操作,例如Textbox、Groupbox,这些操作是通用的,可以在任何应用程序中使用

我开始编写一些单元测试,只是想知道我应该使用System.Windows.Forms中的真正表单控件,还是应该模拟我要测试的部分。例如:

假设我有一个接受控件的方法,如果它是一个文本框,它将清除text属性,如下所示:

        public static void clearall(this Control control)
        {
            if (control.GetType() == typeof(TextBox))
            {
                ((TextBox)control).Clear();
            }
        }
        [TestMethod]
        public void TestClear() 
        {
            List<Control> listofcontrols = new List<Control>();
            TextBox textbox1 = new TextBox() {Text = "Hello World" };
            TextBox textbox2 = new TextBox() { Text = "Hello World" };
            TextBox textbox3 = new TextBox() { Text = "Hello World" };
            TextBox textbox4 = new TextBox() { Text = "Hello World" };

            listofcontrols.Add(textbox1);
            listofcontrols.Add(textbox2);
            listofcontrols.Add(textbox3);
            listofcontrols.Add(textbox4);

            foreach (Control control in listofcontrols)
            {
                control.clearall();
                Assert.AreEqual("", control.Text);
            }
        }
public interface ITextBox
{
    public string Text {get; set;}
}

public class TextBoxAdapter : ITextBox
{
    private readonly System.Windows.Forms.TextBox _textBox;
    public TextBoxAdapter(System.Windows.Forms.TextBox textBox)
    {
        _textBox = textBox;
    }

    public string Text
    {
        get { return _textBox.Text; }
        set { _textBox.Text = value; }
    }
}

public class YourClass
{
    private ITextBox _textBox;
    public YourClass(ITextBox textBox)
    {
        _textBox = textBox;
    }

    public void DoSomething()
    {
        _textBox.Text = "twiddleMe";
    }
}
然后我想测试这个方法,所以我做了如下操作:

        public static void clearall(this Control control)
        {
            if (control.GetType() == typeof(TextBox))
            {
                ((TextBox)control).Clear();
            }
        }
        [TestMethod]
        public void TestClear() 
        {
            List<Control> listofcontrols = new List<Control>();
            TextBox textbox1 = new TextBox() {Text = "Hello World" };
            TextBox textbox2 = new TextBox() { Text = "Hello World" };
            TextBox textbox3 = new TextBox() { Text = "Hello World" };
            TextBox textbox4 = new TextBox() { Text = "Hello World" };

            listofcontrols.Add(textbox1);
            listofcontrols.Add(textbox2);
            listofcontrols.Add(textbox3);
            listofcontrols.Add(textbox4);

            foreach (Control control in listofcontrols)
            {
                control.clearall();
                Assert.AreEqual("", control.Text);
            }
        }
public interface ITextBox
{
    public string Text {get; set;}
}

public class TextBoxAdapter : ITextBox
{
    private readonly System.Windows.Forms.TextBox _textBox;
    public TextBoxAdapter(System.Windows.Forms.TextBox textBox)
    {
        _textBox = textBox;
    }

    public string Text
    {
        get { return _textBox.Text; }
        set { _textBox.Text = value; }
    }
}

public class YourClass
{
    private ITextBox _textBox;
    public YourClass(ITextBox textBox)
    {
        _textBox = textBox;
    }

    public void DoSomething()
    {
        _textBox.Text = "twiddleMe";
    }
}
[TestMethod]
public void TestClear()
{
控件列表=新列表();
TextBox textbox1=new TextBox(){Text=“Hello World”};
TextBox textbox2=new TextBox(){Text=“Hello World”};
TextBox textbox3=new TextBox(){Text=“Hello World”};
TextBox textbox4=new TextBox(){Text=“Hello World”};
控件列表。添加(文本框1);
控件列表。添加(文本框2);
控件列表。添加(文本框3);
控件列表。添加(文本框4);
foreach(控件列表中的控件)
{
control.clearall();
Assert.AreEqual(“,control.Text”);
}
}
我应该在单元测试中添加对System.Window.Forms的引用并使用真实的Textbox对象吗?还是我做错了


注意:上面的代码只是一个示例,我没有编译或运行它。

如果您试图通过模拟与UI控件的交互来对应用程序逻辑进行单元测试,则应该使用。然后,您就可以拥有一个存根视图,并从单元测试中调用控制器方法


如果您试图进行单元测试的是实际的控件,那么我就明白了。

如果您的代码依赖于System.Windows.Forms.Control,那么您所建议的甚至不会编译。您的控件版本和文本框的类型完全错误

相反,如果您将UI和逻辑与接口分离,那么您可以这样做。。。大概是这样的:

        public static void clearall(this Control control)
        {
            if (control.GetType() == typeof(TextBox))
            {
                ((TextBox)control).Clear();
            }
        }
        [TestMethod]
        public void TestClear() 
        {
            List<Control> listofcontrols = new List<Control>();
            TextBox textbox1 = new TextBox() {Text = "Hello World" };
            TextBox textbox2 = new TextBox() { Text = "Hello World" };
            TextBox textbox3 = new TextBox() { Text = "Hello World" };
            TextBox textbox4 = new TextBox() { Text = "Hello World" };

            listofcontrols.Add(textbox1);
            listofcontrols.Add(textbox2);
            listofcontrols.Add(textbox3);
            listofcontrols.Add(textbox4);

            foreach (Control control in listofcontrols)
            {
                control.clearall();
                Assert.AreEqual("", control.Text);
            }
        }
public interface ITextBox
{
    public string Text {get; set;}
}

public class TextBoxAdapter : ITextBox
{
    private readonly System.Windows.Forms.TextBox _textBox;
    public TextBoxAdapter(System.Windows.Forms.TextBox textBox)
    {
        _textBox = textBox;
    }

    public string Text
    {
        get { return _textBox.Text; }
        set { _textBox.Text = value; }
    }
}

public class YourClass
{
    private ITextBox _textBox;
    public YourClass(ITextBox textBox)
    {
        _textBox = textBox;
    }

    public void DoSomething()
    {
        _textBox.Text = "twiddleMe";
    }
}
然后,在您的测试中,您所需要做的就是创建一个mock、fake或stub ITextBox并传递它

当我这样做的时候,我会在稍微高一点的级别上创建一个接口。。。我创建了一个与整个UI非常相似的界面,并让UI实现该界面。然后,我可以随心所欲地摆弄UI,而不必真正知道它是一个表单控件


顺便说一下,如果你想使用创建实际控件的方法,请考虑这篇博客文章:

< P>有几种模式可用于将UI演示与UI逻辑分离,包括模型视图控制器和模型视图演示者的各种化身(Akable对话框)。Humble Dialog是专门为简化单元测试而设计的。您的设计库中肯定应该有一个这样的UI模式

但是我发现,对于简单的表单,当框架支持它时,直接针对真实的UI控件进行测试非常简单。我已经在JavaSwing和Windows.Forms中构建了非常健壮的UI,完全测试第一。我无法在SWT或ASP.NET中管理它,并恢复为MVP

为了测试像这样的东西

[Test] public void ShouldCopyFromAvailableToSelectedWhenAddButtonIsCLicked(){
  myForm.AvailableList.Items.Add("red");
  myForm.AvailableList.Items.Add("yellow");
  myForm.AvailableList.Items.Add("blue");

  myForm.AvailableList.SelectedIndex = 1;
  myForm.AddButton.Click();

  Assert.That(myForm.AvaiableList.Items.Count, Is.EqualTo(2));
  Assert.That(myForm.SelectedList.Items[0], Is.EqualTo("yellow"));
}

…直接针对UI控件进行操作效果良好。但是,如果您想开始测试鼠标移动、按键或拖放,最好选择Brian建议的更健壮的UI模式。

我同意,测试.NET Framework没有实际用途。微软已经在很大程度上做到了这一点:)。