views:

518

answers:

4

Problem

VS2010 and TFS2010 support creating so-called Coded UI Tests. All the demos I have found, start with the WPF application already running in the background when the Coded UI Test begins or the EXE is started using the absolute path to it.

I, however, would like to start my WPF application under test from the unit test code. That way it'll also work on the build server and on my peer's working copies.

How do I accomplish that?

My discoveries so far

a) This post shows how to start a XAML window. But that's not what I want. I want to start the App.xaml because it contains XAML resources and there is application logic in the code behind file.

b) The second screenshot on this post shows a line starting with

ApplicationUnterTest calculatorWindow = ApplicationUnderTest.Launch(...);

which is conceptually pretty much what I am looking for, except that again this example uses an absolute path the the executable file.

c) A Google search for "Programmatically start WPF" didn't help either.

A: 
MyProject.App myApp = new MyProject.App();
myApp.InitializeComponent();
myApp.Run();
Lernkurve
+1  A: 

To start an App, it's as simple as Lernkurve suggests.

However, it you're starting it in a unit testing framework, you'll need to run that code in a different thread. The details can be...interesting. You might want to check out this tool: IcuTest

Hatz
+2  A: 

I am doing something similar in VS2008 and manually creating the tests using UI Spy to help me identify the controls and some helper methods, not shown, to trigger button clicks and verify values on the screen. I use the Process object to launch the application I am testing in the TestInitialize method and in the TestCleanup method I close the process. I have a number of ways to ensure the process has completely closed in the CleanUp. As for the absolute path problem, I just programmatically lookup the current path and append my application's executable. Since I don't know how long it takes for the application to start I put an AutomationId in my main window and set it to "UserApplicationWindow" and wait for that to be visible, of course, you may have something else you could wait for. Finally, I use the MyTestClass as a base class and extend the class for different tests.

[TestClass]
public class MyTestClass
{
    private Process _userAppProcess;
    private AutomationElement _userApplicationElement ;

    /// <summary>
    /// Gets the current directory where the executables are located.  
    /// </summary>
    /// <returns>The current directory of the executables.</returns>
    private static String GetCurrentDirectory()
    {
        return Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).AbsolutePath).Replace("%20", " ");
    }

    [TestInitialize]
    public void SetUp()
    {
        Thread appThread = new Thread(delegate()
        {
            _userAppProcess = new Process();
            _userAppProcess.StartInfo.FileName =GetCurrentDirectory() + "\\UserApplication.exe";
            _userAppProcess.StartInfo.WorkingDirectory = DirectoryUtils.GetCurrentDirectory();
            _userAppProcess.StartInfo.UseShellExecute = false;
            _userAppProcess.Start();
        });
        appThread.SetApartmentState(ApartmentState.STA);
        appThread.Start();

        WaitForApplication();
    }

    private void WaitForApplication()
    {
        AutomationElement aeDesktop = AutomationElement.RootElement;
        if (aeDesktop == null)
        {
            throw new Exception("Unable to get Desktop");
        }

        _userApplicationElement = null;
        do
        {
            _userApplicationElement = aeDesktop.FindFirst(TreeScope.Children,
                new PropertyCondition(AutomationElement.AutomationIdProperty, "UserApplicationWindow"));
            Thread.Sleep(200);
        } while ( (_userApplicationElement == null || _userApplicationElement.Current.IsOffscreen) );

    }

    [TestCleanup]
    public void CleanUp()
    {
        try
        {
            // Tell the application's main window to close.
            WindowPattern window = _userApplicationElement.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern ;
            window.Close();
            if (!_userAppProcess.WaitForExit(3000))
            {
                // We waited 3 seconds for the User Application to close on its own.  
                // Send a close request again through the process class.
                _userAppProcess.CloseMainWindow();
            }

            // All done trying to close the window, terminate the process
            _userAppProcess.Close();
            _userAppProcess = null; 
        }
        catch (Exception ex)
        {
            // I know this is bad, but catching the world is better than letting it fail.
        }
    }
}
RodKnee
Thanks! I like the WaitForApplication() part.
Lernkurve
A: 

I ended up using ApplicationUnderTest.Launch(...) (MSDN) which is automatically created when recording an automated test with Microsoft Test Manager.

Lernkurve