简体   繁体   English

如何在Windows窗体代码后面编写方法的单元测试

[英]How to write unit test for method in back of windows form code

I have method I want to test and I get this error "Invoke or BeginInvoke cannot be called on a control until the window handle has been created." 我有要测试的方法,并且收到此错误“在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke”。 Now I also have a list box that is being populated inside that function I am testing. 现在,我还有一个正在测试的函数内部的列表框。 So its an issue when I separate the Method to another class. 因此,当我将Method分离到另一个类时,这是一个问题。

I understand this because of the form needs to run first, but any alternatives? 我了解这一点,因为表单需要首先运行,但是还有其他选择吗?

public partial class ImportForm : Form
{
    public ImportForm()
    {
    }
    public bool Test(string[] fileNames)//Method to test
    {
        foreach (DataTable table in result.Tables)
        {
            foreach (DataRow dr in table.Rows)
            {
                if (!db.CouncilRefundCases.Any(
                        c => c.RequestReference == dr.ItemArray[1].ToString()))
                {
                    CouncilRefundCase data = new CouncilRefundCase()
                    {
                        FileId = fileId,
                        RequestReference = Convert.ToString(dr.ItemArray[1]),
                        CancelReason = Convert.ToString(dr.ItemArray[2]),
                        ProcessStatusId = (int?)ProcessStatus.Unprocessed,
                        ProcessDescription = new EnumHelper().GetDescription(ProcessStatus.Unprocessed),
                        DateCaptured = DateTime.Now
                    };

                    db.CouncilRefundCases.InsertOnSubmit(data);

                    //Succeeded ones
                    var item = new ListViewItem(dr.ItemArray[1].ToString());
                    lstSuccessSummary.Invoke((Action)delegate
                    {
                        lstSuccessSummary.Items.Add(item);
                    });
                }
                else
                {
                    //Failed ones
                    var item = new ListViewItem(dr.ItemArray[1].ToString());
                    lstSummary.Invoke((Action)delegate
                    {
                        lstSummary.Items.Add(item);
                    });
                }
            }
        }
        return true;
    }
}

Here is my unit test 这是我的单元测试

[TestMethod]
public void TestTest()
{
    bool results=false;
    var files = new string[4];
    files[0] = @"filename1.xlsx";
    files[1] = @"filename2.xlsx";

    ImportForm form= new ImportForm();
     results = form.Test(files);

    Assert.AreEqual(true, results);
}

It's hard to tell without the actual code but usually this indicates that the code in the Test method shouldn't be in the Form. 没有实际的代码很难说,但是通常这表明Test方法中的代码不应该在Form中。

Forms should really just be the display logic, not model logic. 表单实际上应该只是显示逻辑,而不是模型逻辑。

Now I also have a list box that is being populated inside that function I am testing. 现在,我还有一个正在测试的函数内部的列表框。 So its an issue when I separate the Method to another class. 因此,当我将Method分离到另一个类时,这是一个问题。

If you describe the problem with some code, we may be able to help with that. 如果您用一些代码描述问题,我们可能会提供帮助。 You might solve it with events or delegates, to keep the view logic out of your model, and vice-versa. 您可以使用事件或委托来解决它,以使视图逻辑脱离模型,反之亦然。

Note - Before you read the answer 注意-阅读答案之前

In general it's not good idea to couple UI code and business logic tightly, but in case which you are faced with a code which cannot be refactored to be decoupled from UI, you can use the following solution to solve the problem. 通常,紧紧耦合UI代码和业务逻辑不是一个好主意,但是如果您遇到无法重构为与UI分离的代码,则可以使用以下解决方案来解决该问题。

The Problem and The Solution 问题与解决方案

Before showing the form, the form and its controls are not in Created state and you can not use Invoke method of the form or its controls. 在显示表单之前,表单及其控件未处于“ Created状态,并且您不能使用表单或其控件的Invoke方法。

To solve the problem, you can force the form and its controls to be created. 要解决此问题,您可以强制创建表单及其控件。 To do so, it's enough to call internal CreateControl(bool fIgnoreVisible) method of your form and pass true to it as parameter: 为此,只需调用表单的内部CreateControl(bool fIgnoreVisible)方法并将true作为参数传递给它即可:

var f = new Form1();
var createControl = f.GetType().GetMethod("CreateControl",
    BindingFlags.Instance | BindingFlags.NonPublic);
createControl.Invoke(f, new object[] { true });

Alternative solutions 替代解决方案

  • Showing the Form before calling the method. 在调用方法之前显示Form Then the form will be shown during running unit tests. 然后在运行单元测试期间将显示该表格。

  • Showing the Form in a STA thread. STA线程中显示Form

Example

Let's say you have such method in your form: 假设您的表单中有这样的方法:

public partial class Form1 : Form
{
    //...
    public int Method1(int i)
    {
        this.Invoke(new Action(() => { i++; }));
        return i;
    }
}

Then in your test project, you can use the following code: 然后,在测试项目中,可以使用以下代码:

[TestMethod]
public void TestMethod1()
{
    var f = new Form1();

    var createControl = f.GetType().GetMethod("CreateControl",
        BindingFlags.Instance | BindingFlags.NonPublic);
    createControl.Invoke(f, new object[] { true });

    var input = 0;
    var expected = 1;
    var actual = f.Method1(input);
    Assert.AreEqual(expected, actual);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM