2

When I run a ShowDialog call from within a Unit test, I'm getting a

System.Windows.Markup.XamlParseException: The current SynchronizationContext may not be used as a task scheduler.

Running from main works fine. I realize this isn't the normal use of a tester, but it's valuable to be able to pick a dialog and click a button to run it with test data, to see if the dialog works. However this bug prevents me from running it.

Is there maybe a trick to run the dialog code on the "proper" thread?

Here's the Unit testing code:

[Fact]
static public void Draw2()
{
  var uiThread = new Thread(Draw20);
  uiThread.SetApartmentState(ApartmentState.STA);
  uiThread.Start();
  // Wait for the UI thread to finish
  uiThread.Join();
}
  static void Draw20()
  {
    ModernUIApp1.MainWindow window = new MainWindow();

    System.Windows.Forms.Integration
      .ElementHost
      .EnableModelessKeyboardInterop(window);
    window.ShowDialog();
    window = null;
  }

Window code: >

    <mui:ModernWindow.MenuLinkGroups>
    <mui:LinkGroup DisplayName="welcome">
      <mui:LinkGroup.Links>
        <mui:Link DisplayName="home" Source="/Pages/Home.xaml" />
        <mui:Link DisplayName="my page" Source="/Pages/BasicPage.xaml" />
      </mui:LinkGroup.Links>
    </mui:LinkGroup>
    <mui:LinkGroup DisplayName="settings" GroupName="settings">
            <mui:LinkGroup.Links>
                <mui:Link DisplayName="software" Source="/Pages/Settings.xaml" />
            </mui:LinkGroup.Links>
        </mui:LinkGroup>
    </mui:ModernWindow.MenuLinkGroups>

    <mui:ModernWindow.TitleLinks>
        <mui:Link DisplayName="settings" Source="/Pages/Settings.xaml" />
    </mui:ModernWindow.TitleLinks>
</mui:ModernWindow>
Alan Baljeu
  • 2,383
  • 4
  • 25
  • 40

3 Answers3

1

In order to show WPF UI from a unit test, you'll need to spawn a new thread, ensure it's single-threaded apartment (STA), create the UI from that thread, show the dialog, then marshal the results back to the unit test thread.

var uiThread = new Thread(MyCreateAndShowUIFunction);
uiThread.SetApartmentState(ApartmentState.STA);
uiThread.Start();

...

// Wait for the UI thread to finish
uiThread.Join();

By and large, unit tests aren't intended to actually launch the UI. That would be an integration test, or even a coded UI test.

Community
  • 1
  • 1
Judah Gabriel Himango
  • 58,906
  • 38
  • 158
  • 212
  • I tried this code, but the same issue arises in the new dialog thread. Now about your comment, enlighten me: What tool should be used for integration test or coded ui test, and why is that better than a unit tester? – Alan Baljeu Nov 04 '13 at 03:25
  • Show us the code you're using; you should be able to test the UI in a unit test if you spawn an STA thread to create and deal with the UI. – Judah Gabriel Himango Nov 04 '13 at 03:27
  • So when you create your main window and all other UI stuff? You need to put that inside MyCreateAndShowUIFunction like I show above. – Judah Gabriel Himango Nov 04 '13 at 04:52
  • I did Judah, it doesn't help. Still the SynchronozationContext error. – Alan Baljeu Nov 04 '13 at 05:20
0

Searched around for this error, and it looks like you may need to set your own sync context:

[SetUp]
public void TestSetUp()
{
  SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
Community
  • 1
  • 1
Judah Gabriel Himango
  • 58,906
  • 38
  • 158
  • 212
0

I suspect this is a problem with your unit test runner or some code within the dialog, as I can run your code just fine with ReSharper's xUnit runner. My dialog contains only a button to set the DialogResult, and the test completes without any exceptions.

You could try running this on your UI thread before creating the dialog:

SynchronizationContext.SetSynchronizationContext(
    new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher)
);

If this does not work, try combining it with @JudahHimango's answer of creating a SynchronizationContext for the test thread.

Mike Strobel
  • 25,075
  • 57
  • 69
  • I tried the code you suggested on the UI thread, no luck. As for your test, I can also get a button working. What fails is when I add a Home.xaml page to the example. – Alan Baljeu Nov 04 '13 at 16:22
  • Well, that certainly narrows it down. Are you calling any code in that page that uses the `System.Threading.Tasks` APIs or `async/await`, perhaps indirectly? Also, did you try combining my idea with Judah's idea of setting `SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());` for the test thread? – Mike Strobel Nov 04 '13 at 16:31