【发布时间】:2015-12-14 15:37:13
【问题描述】:
考虑对 WinApi 功能进行以下单元测试:
public class WinApiTest
{
[TestMethod]
public void WinApiFindFormTest_SimpleNesting()
{
var form = new Form();
form.Text = @"My form";
var button = new Button();
button.Text = @"My button";
form.Controls.Add(button);
//with below line commented out, the test fails
form.Show();
IntPtr actualParent = WinApiTest.FindParent(button.Handle);
IntPtr expectedParent = form.Handle;
//below 2 lines were added for debugging purposes, they are not part of test
//and they don't affect test results
Debug.WriteLine("Actual: " + WinApi.GetWindowTitle(actualParent));
Debug.WriteLine("Expected: " + WinApi.GetWindowTitle(expectedParent));
Assert.AreEqual(actualParent, expectedParent);
}
//this is a method being tested
//please assume it's located in another class
//I'm not trying to test winapi
public static IntPtr FindParent(IntPtr child)
{
while (true)
{
IntPtr parent = WinApi.GetParent(child);
if (parent == IntPtr.Zero)
{
return child;
}
child = parent;
}
}
}
问题是要让它工作,我必须显示表单,即执行form.Show(),否则,它会失败并显示以下输出:
Actual: WindowsFormsParkingWindow
Expected: My form
Exception thrown: 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException' in Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll
我读到过这个神秘的WindowsFormsParkingWindow,它似乎只有在没有指定父级时才有意义。因此,所有没有父控件的控件都位于此窗口下。然而,在我的例子中,button 被明确指定为form 控件的一部分。
问题:是否有适当的方法可以通过此测试?我正在尝试测试FindParent 方法。本着真正的单元测试精神,任何东西都不应该突然出现在用户面前。可以使用 Show 和 Hide 序列,但我认为这是解决问题的一种相当 hack-ish 的方法。
下面提供了 WinApi 类的代码 - 它并没有为问题增加太多价值,但如果你绝对必须看到它,那就去吧(大部分来自 this answer on SO):
public class WinApi
{
/// <summary>
/// Get window title for a given IntPtr handle.
/// </summary>
/// <param name="handle">Input handle.</param>
/// <remarks>
/// Major portition of code for below class was used from here:
/// https://stackoverflow.com/questions/4604023/unable-to-read-another-applications-caption
/// </remarks>
public static string GetWindowTitle(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
throw new ArgumentNullException(nameof(handle));
}
int length = WinApi.SendMessageGetTextLength(handle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
if (length > 0 && length < int.MaxValue)
{
length++; // room for EOS terminator
StringBuilder windowTitle = new StringBuilder(length);
WinApi.SendMessageGetText(handle, WM_GETTEXT, (IntPtr)windowTitle.Capacity, windowTitle);
return windowTitle.ToString();
}
return String.Empty;
}
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern IntPtr GetParent(IntPtr hWnd);
}
【问题讨论】:
-
@HansPassant:请查看我更新的问题,我添加了更多信息,希望现在更清楚。
FindParent是正在测试的方法,而不是 WinApi。 -
汉斯已经回答了你的问题。我不明白你为什么不接受这个答案。
-
@DavidHeffernan:
You'll have to write your unit test to accommodate this behavior如何 我可以让它工作,而不是显示/隐藏。如果没有更好的方法,直接说出来 - 是的,不幸的是,显示/隐藏是这里最好的方法。然后在cmets中汉斯问是否This is supposed to test WM_GETTEXT?,表示他一开始没看懂问题。 -
汉斯回答得很好,你没听懂。我很清楚你需要做什么。我理解汉斯为什么撤回他的帮助。
-
我的意思是你应该清楚你需要确保父级创建了句柄。
标签: c# winforms winapi pinvoke mstest