问题
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." 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.
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);
}
回答1:
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.
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.
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:
var f = new Form1();
var createControl = f.GetType().GetMethod("CreateControl",
BindingFlags.Instance | BindingFlags.NonPublic);
createControl.Invoke(f, new object[] { true });
Alternative solutions
Showing the
Formbefore calling the method. Then the form will be shown during running unit tests.Showing the
Formin aSTAthread.
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);
}
回答2:
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.
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.
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.
来源:https://stackoverflow.com/questions/50691935/how-to-write-unit-test-for-method-in-back-of-windows-form-code