Proposed Unit Testing with UI objects in Windows 8 Consumer Preview

  • Thursday, March 15, 2012 12:41 PM
     
      Has Code

    I am currently using the VS11/MSTest unit testing integration to test a Windows 8 Metro-style class library. In one set of tests I wish to create a mock object that derives from FrameworkElement. Unfortunately you cannot create any such UI elements on non-UI threads. Does anyone have any idea how to either run uint-tests via a CoreDispatcher (as far as I can tell you can only access this from an existing UI object), or to be able to create FrameworkElement sub-classes in a unit test?

    Thanks,

        Andrew

    Futher information:

    The scenario I am trying to unit test is as such. I have a method to test that accepts a FrameworkElement as a parameter and alters the DataContext based upon a set of rules. My test therefore looks similar to,

    [TestMethod]
    public void MyTest()
    {
        MyService service = new MyService();
        MockElement element = new MockElement();
        
        service.SetDataContext(element);
        Assert.AreEqual(element.DataContext, ...);
    }
    public class MockElement : FrameworkElement
    {
        ...
    }
    Since the test does not run on the UI thread, I get an exception when trying to create a new MockElement instance.

All Replies

  • Monday, March 26, 2012 4:09 PM
     
     

    Hi Andrew,

    Can you try moving the UI related actions to a different thread ( using UI Thread dispatcher). You can use Async test methods if you need to wait on the UI thread.

    Make sure to catch the exceptions of the UI thread from within the same thread. <o:p></o:p>

    Thanks

    Bhuva


  • Sunday, April 01, 2012 3:42 PM
     
     

    Hi Bhuva,

    If I was working with WPF then this is the usual route I would have taken. However with Metro style applications how do you get access to the UI Thread dispatcher?

    • There is no static property on CoreDispatcher to get the current dispatcher
    • If I am in the context of the application this is available from the 'Dispatcher' property of any UI element, however I am stuck here as I can't create any UI elements to get the CoreDispatcher
    • The Window.Current property is 'null'

    Is there another way to access this.

    Thanks,

        Andrew

    • Proposed As Answer by Vikram Agrawal Thursday, April 26, 2012 6:14 PM
    • Unproposed As Answer by Vikram Agrawal Thursday, April 26, 2012 6:15 PM
    •  
  • Thursday, April 26, 2012 5:51 PM
     
      Has Code
    
    
    

    You can use UI thread dispatcher (in VS11 Beta) as follows

    Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.Invoke(
    Windows.UI.Core.CoreDispatcherPriority.Normal,
    (s,a) => {     // Your UI code },
    this,
    null);

    Note: Any Assert failure or Exception thrown in code executed with UI thread dispatcher will be just ignored causing false positive. If possible use Assert outside UI thread or use following approach (this will give you failure details, but actual stack trace will be lost)

    [TestMethod]
    public void TestMethod1() 
    {
        Exception failure = null;
    
        Windows.ApplicationModel.Core.CoreApplication.
        MainView.CoreWindow.Dispatcher.Invoke(
            Windows.UI.Core.CoreDispatcherPriority.Normal,
            (s,a) => 
            {
                try 
                {
                    // Your UI code. May include Asserts
                }
                catch (Exception ex)
                {
                    failure = ex;
                }
            },
            this,
            null);
    
        if (failure != null)
        {
            throw failure;
        }
    }


    Regards,
    Vikram Agrawal,
    Developer, VSTLM, Microsoft Corporation


  • Friday, April 27, 2012 8:28 PM
     
      Has Code

    Thank you. This worked very nicely.

    Will there be a way to support this automatically? Ideally, I could add an attribute to the class method which would cause the test method, test initialize and test cleanup methods to all run on the UI thread. Also, since I have to re-throw the exception, the stack trace for the true exception is not shown, so I don’t get hyperlinks to the true source of the exception.

    Compiling your code, I get the warning:

    warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

    I was able to correct the warning by changing the function signature to remove the async.

    public void TestMethod5()

    Test Code I end up using based on your example:

    [TestClass]
    public class UnitTest1
    {
    public TestContext TestContext {get;set;}
    public void ExecuteOnUI(Action fn)
    {
        Exception failure = null;
    
        Windows.ApplicationModel.Core.CoreApplication.
        MainView.CoreWindow.Dispatcher.Invoke(
            Windows.UI.Core.CoreDispatcherPriority.Normal,
            (s, a) =>
            {
                try
                {
                    fn();
                }
                catch (Exception ex)
                {
                    failure = ex;
                }
            },
            this,
            null);
    
        if (failure != null)
        {
            TestContext.WriteLine(failure.StackTrace);
            throw failure;
        }
    
    }
    
    [TestInitialize]
    public void TestInitialize()
    {
        ExecuteOnUI(() =>
        {
            testTextBlock = new TextBlock();
        });
    }
    
    
    TextBlock testTextBlock;
    [TestMethod]
    public void TestMethod5()
    {
        ExecuteOnUI(() =>
            {
                testTextBlock.Text = "new text";
            });
    }
    
    
    }
    
    
    


  • Saturday, April 28, 2012 1:55 AM
     
     

    Hi Michael,

    Thanks for your feedback and pointing out mistake in sample code. I fixed the code. We have task for executing test methods in UI thread in our backlog. Depending on factors we are hoping to add it in one of future releases.


    Regards, Vikram Agrawal, Developer, VSTLM, Microsoft Corporation

  • Friday, May 18, 2012 9:58 AM
     
     Proposed Has Code

    Hi Andy / Michael,

    I got a better way to handle this without loosing stack trace. RunAsync can used for the purpose instead of Invoke.

    public IAsyncAction ExecuteOnUIThread(Windows.UI.Core.DispatchedHandler action)
    {
        return Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, action);
    }
    
    [TestMethod]
    public async Task TestMethod1() 
    {
        await ExeuteOnUIThread(()=>
        {
            // Code to execute on UI thread
        });
    }

    This will preserve the exception and stack trace. Any Assert failure or Exception in code getting executed on UI thread will be captured properly and stack trace with proper file/line info will come in "Unit Test Explorer"


    Regards,
    Vikram Agrawal,
    Developer, VSTLM, Microsoft Corporation