In my last article (https://www.codemag.com/Article/1901071/10-Reasons-Why-Unit-Testing-Matters), I presented 10 reasons why unit testing matters. In this article, I tackle the challenge of unit testing code that has interactive user interface dialogs requiring the user to provide a response. Given that unit tests must be automated, that fact may lead you to conclude that code requiring user prompts can't be unit tested because you can't use a build server to automate the response to a modal prompt.

It's indeed true that you can't use a build server to automate a response to a model prompt. Nevertheless, it's not true that the presence of user prompts is an absolute bar to unit testing. Depending on your implementation, unit testing may be barred and whether your code is barred from unit testing depends on whether your code is unit-testable.

The question is how to write code that requires a user prompt that's unit testable. The high-level answer is dependency inversion.

What follows is a brief exercise in applying dependency inversion to scenarios where a user-prompt is required. This article assumes that you have a basic understanding of unit testing and mocking concepts and libraries. If you don't, I suggest that you take a brief detour and acquaint yourself with those concepts. A good primer is my unit testing whitepaper on LinkedIn: https://www.linkedin.com/pulse/advanced-unit-testing-whitepaper-john-petersen. This article's focus is how to apply unit testing and mocking concepts in a specific context.

Tools and Concepts

The examples presented in this article are based on a very simple XAML/WPF solution that implements the MVVM (Model, View, View Model) pattern. The MVVM pattern is the chosen vehicle because of its amenability to unit testing. That said, the pattern's presence alone doesn't guarantee that your code is unit-testable.

The sample uses the MVVM-Light framework and the Unity Inversion of Control (IoC) Container. For the purpose of this article, I'm using the MVVM-Light IoC Container and Unity's Common Service Locator facility. Despite using these tools, although MVVM is supported, you can't unit test the functionality. The functionality in this case, illustrated in Figure 1, is a simple form with a button that prompts the user to update the form's caption with the current date and time:

Figure 1      :       If the user clicks yes in the prompt, the form caption is updated to reflect the current date and time.
Figure 1 : If the user clicks yes in the prompt, the form caption is updated to reflect the current date and time.

Figure 2 illustrates the XAML and the view model code you want to unit test:

Figure       2      :       In the MVVM pattern, a view's functionality is hosted in a view model that's bound to specific view elements.
Figure 2 : In the MVVM pattern, a view's functionality is hosted in a view model that's bound to specific view elements.

As the ButtonClick code is currently situated, it's not unit-testable because you can't isolate its function from the message box implementation. In other words, there's hard dependency on the message box. It's important to note that the application uses the Unity IoC Container as a way to implement MVVM. Figure 3 illustrates how Unity IoC fits into the equation:

Figure       3      :       The view's DataContext Property is hydrated with a DialogViewModel instance obtained from the IoC container.
Figure 3 : The view's DataContext Property is hydrated with a DialogViewModel instance obtained from the IoC container.

An IoC container isn't a required element to implement MVVM. I've included it here as part of the MVVM implementation to illustrate that the mere presence of an IoC container, which is an application of the Dependency Inversion Principle doesn't by itself mean that your application is necessarily unit testable. MVVM facilitates a way to avoid directly implementing code in the user interface. Such code is referred to as “code behind.”

One question that's often asked with unit testing is where to draw the line. In other words, what aspects of your application are not candidates for unit testing? In my opinion, anything framework-related is not a candidate for unit testing. In this case, it includes the MVVM-Light, Unity, and the .NET Framework itself. Presumably, the source code projects for those items have such tests. I'd never waste time on verifying whether MessageBox.Show works. That's a framework item that I presume works. Because of that presumption, there's no need to unit test. For things where you can't make such a presumption, that's where unit testing comes into play. In this case, I'm not interested in whether MessageBox works. Rather, I'm interested in how my view model's ButtonClick Method behaves in response to MessageBox.

When attention turns to unit testing, specific tools used include the MSTest unit testing library and the Moq mocking library.

The View Model's ButtonClick Method V1

Figure 4 illustrates the ButtonClick's initial state.

Figure       4      :       The ButtonClick method is not unit testable because it has a direct dependency on MessageBox.
Figure 4 : The ButtonClick method is not unit testable because it has a direct dependency on MessageBox.

The good news is that the ButtonClick method works. The bad news is that there's no way to independently verify that it works without running the application. There are two major problems with this code and one minor problem. The first major issue is the direct dependency on DateTime.Now in the constructor. The second major issue is the direct dependency that the ButtonClick method has on MessageBox. A third issue is the ButtonClick's lack of compliance with the Single-Responsibility Principle. In a word, the code isn't clean. In the next section, we'll-make it better.

All software is eventually tested. Once your software is placed into production, in the course of your end-user's interaction with your software, they're continuously testing your code. End-users typically don't comprehend their role in testing, even when they report bugs. What they do comprehend are their feelings that may range from minor annoyance to outright frustration because your application doesn't work, which in turn may prevent people from being able to perform their jobs. This is how minor issues can escalate to big problems. Whether the issue is discovered during development or by the end-user in production, the core issue is the same. The derivative issues are magnified because of the context.

Unit testing allows you to remediate minor issues before they grow to major problems. The problems aren't all technical. Issues with trust confidence can result. Your end-user customers must have trust that the software operates as specified. Those same end-user customers must have confidence that you can deliver such software.

From a functional standpoint, the following is the specification for the ButtonClick Method (in the context of the user interface): When the user clicks the button and responds Yes to the prompt, the form's caption is updated with the current DateTime value.

Although this is a very trivial scenario for illustration purposes, end-users do click buttons and expect a response all the time. The question is how you can verify the ButtonClick's function in an automated unit-test. The answer is to refactor the code in a way that applies dependency inversion to the View model. You have a vehicle to facilitate dependency inversion, the Unity Inversion of Control Container.

Unit testing allows you to remediate minor issues before they grow to major problems, not all of which are technical. Your end-user customers must trust that the software operates as specified and confidence that you can deliver such software.

The View Model's ButtonClick Method V2

Figure 5 illustrates a better version of our view model.

Figure       5      :       Removing direct dependencies on DateTime.Now and MessageBox makes the DialogViewModel unit testable.
Figure 5 : Removing direct dependencies on DateTime.Now and MessageBox makes the DialogViewModel unit testable.

The View Model no longer has a direct dependency on DateTime.Now and MessageBox. Using Dependency Injection, which is facilitated with the Inversion of Control Container illustrated in Figure 6, you now have a clear separation of concerns that makes the ButtonClick method unit testable. By abstracting the MessageBox functionality away from the view model, I also took the opportunity to simplify the interface. No longer does the view model have to be concerned with MessageBoxButtons or DialogResult classes.

Figure       6      :       In order to have dependencies automatically injected when the view model is created, entries must be made in the container.
Figure 6 : In order to have dependencies automatically injected when the view model is created, entries must be made in the container.

In addition, because the View Model no longer has a direct dependency on DateTime.Now, you can make “now” any datetime value you want. In unit testing, it's necessary that you're able to set the data to a specific desired state. If you've ever had unit tests that perform math on dates suddenly fail after being in service for a while, check to see if DateTime.Now is either in your test or in the code under test.

The ButtonClick no longer contains a separate call to RaisePropertyChange to notify WPF to update the bindings. As an alternative, the WindowCaption property was updated with a new setter to handle that task. Now, the ButtonClick does one thing and one thing only, which is to update the WindowCaption property if the DialogService's Show method returns True, which in turn only happens if the user clicks the Yes button.

Figure 7 illustrates the new DialogService class.

Figure       7      :       The DialogService conforms to one interface, with one method named Show that can be mocked for unit testing.
Figure 7 : The DialogService conforms to one interface, with one method named Show that can be mocked for unit testing.

Figure 8 illustrates the new ClockService class.

Figure       8      :       The ClockService conforms to one interface with one method named Now that can be mocked for unit testing.
Figure 8 : The ClockService conforms to one interface with one method named Now that can be mocked for unit testing.

The MVVM Framework and Unity work together to automatically hydrate the IoC container and to locate the resources necessary to run the application. Referring back to Figure 3, the bootstrapping of resources occurs in the ServiceLocator.Current.GetInstance<DialogViewModel>(); call. GetInstance is a factory method that does the work of bootstrapping the view model. That bootstrapping process involves pinging the IoC Container for the necessary resources.

Creating the Unit Test

Figure 9 illustrates the unit test.

Figure       9      :       The unit test verifies the expected result for each user response scenario.
Figure 9 : The unit test verifies the expected result for each user response scenario.

Because the view model dependencies are manifested as services that conform to an interface, you can use a mocking framework to create mocked objects that are in turn injected into the view model's constructor. These mocked objects are often referred to as test doubles. With mocks, you can control how the methods will behave. As far as the system under test is concerned, the injected objects are the real thing. For the clock service, a set-up sequence is used to support two calls. This is necessary because the implementation has two calls, one in the view model constructor that's always called and one in ButtonClick that may be called, depending on whether the user clicks Yes in the MessageBox dialog.

For purposes of the test, there's no interactive user and there's no actual MessageBox. As far as the view model is concerned, it either receives a True or False response from the service. To facilitate the unit test, the mocked object's Show Method is set up to return True or False. The View model's behavior changes depending on whether True or False is returned. Because you know what the Clock service mock will return, it follows that you can verify whether the ButtonClick's method behaves correctly when the Show method returns True or False.

Summary

Using services and dependency injection, code during runtime that relies on user prompts can nevertheless benefit from automated unit testing with mocks. It's not enough to rely on frameworks and libraries that support unit testing. The patterns and practices applied in such frameworks and libraries must be fully applied to your code.

A question you may have is whether the Dialog and Clock services themselves need to be unit-testable. Because the only thing they do is act as a facade over framework elements, I would argue that for those items, there's no need for unit testing.

It's important to remember that there are other kinds of testing that should be performed before going live. One such test is the functional user acceptance tests (UAT). Until somebody interacts with your software, you won't know for sure whether what you've built will ultimately be acceptable. However, the efficacy of UAT is greatly diminished when the bulk of what it does is concerned with finding defects that could otherwise be found with effective unit tests.