A key benefit of the MVVM pattern is that it allows you to unit test an app without needing to display its UI. Sometimes, however, you may want to verify the behavior of the UI by emulating user interaction. This can be achieved using coded UI tests.
The advantage of coded UI tests is that they provide your app with a test environment that is closer to the real world; they allow you to simulate the tapping of buttons or entering of text while the app is running.
The downside of coded UI tests is that they are tightly coupled to the user interface, which is often the most likely thing to change over time. If the UI is modified, it can mean having to rewrite your coded UI tests.
Coded UI tests can be performed with the UTF by populating a test Panel
control that can host UIElements
during test execution. The PresentationTest
class subclasses the WorkItemTest
class and provides a TestPanel
property that allows you to materialize the page or control that you want to test. Observe the following excerpt:
[TestClass]
public class ChatClientUITests : PresentationTest
{
ChatClientView view;
[TestInitialize]
public void PreparePage()
{
view = new ChatClientView();
TestPanel.Children.Add(view);
}
...
}
Recall that the TestInitialize
attribute causes the method to be called before each test method in the parent class. When the PreparePage
method is called, it creates a new instance of the ChatClientView
and places it into a container control within the test harness.
This section looks at the three test methods in the ChatClientUITests
class.
The first test method simply verifies that the PhoneApplicationPage
is presented in the TestPanel
, as shown:
[TestMethod]
public void ShouldDisplayDefaultSize()
{
Assert.IsTrue(view.ActualWidth > 0);
}
The ShouldDisplayDefaultSize
method demonstrates that you are able to interact with the UI elements from code. In fact, while the tests are running, their actions are viewable within the test harness (see Figure 24.7).
To interact with controls on the page, give the test project access to the internal members of the main project. This is done by placing an InternalsVisibleTo
attribute in the AssemblyInfo
class of the main project, shown in the previous section “Testing Nonpublic Members.”
When the InternalsVisibleTo
is placed in the target project, IntelliSense is enabled for controls on the page.
To test that the Send button is enabled when the message TextBox
is populated, set the text and then perform an assert using the button, as shown in the following excerpt:
[TestMethod]
public void ButtonShouldBeEnabled()
{
view.textBlock_Message.Text = "Test";
Assert.IsTrue(view.button_Send.IsEnabled);
}
The UTF is designed to run tests on a single thread. The UI thread in Windows Phone XAML apps uses a message pump, and often calls to update properties or reactions to property changed events are driven forward using Dispatcher
calls. The result is that a UIElement
may not be in the state that you think it should be when you are performing an assertion. Fortunately the Windows Phone UTF compensates with some built-in asynchronous features.
The Asynchronous
attribute tells the UTF to keep running the test until the test class’s base method, WorkItemTest.EnqueueTestComplete
, is called, or until an unhandled exception occurs. This allows you to spin off work to other threads, or queue work to happen on the UI thread, while keeping the test alive.
The WorkItemTest.EnqueueCallback
method allows actions to be queued on the UI thread. This may include actions such as verifying the value of a UIElement
’s property after a PropertyChanged
event has been given the opportunity to propagate.
The following excerpt demonstrates how to use the Asynchronous
attribute in combination with the EnqueueCallback
method to set the text of a sample view’s message box. It simulates a tap to the Send Message button and then verifies that the ListBox
has been correctly populated:
[TestMethod]
[Asynchronous]
public void ButtonShouldSendMessage()
{
view.textBlock_Message.Text = "Test";
EnqueueCallback(() => ((IAppBarItem)view.button_Send).PerformClick());
EnqueueCallback(() => Assert.IsTrue(view.listBox.Items.Count > 0));
EnqueueTestComplete();
}
When the textBlock_Message.Text
property is set, the Send button is not enabled until the viewmodel’s SendCommand
responds to the Message
property’s PropertyChanged
event. After it has had a chance to respond, the command enables itself.
The EnqueueCallback
method queues the PerformClick
method, so that by the time it is called, the button is enabled. Similarly, verification that the ListBox
has been populated must occur after the UI has responded to the new item in the Messages
collection in the viewmodel.
The UTF is notified that all nonblocking activity has been completed by calling the base class’s WorkItemTest.EnqueueTestComplete
method.
3.145.56.28