The CODE Framework provides a set of tools for developing n-tier, service-oriented applications. In addition, there is a set of recommended patterns and practices that provide guidance in enabling developers to achieve Rapid Application Development, Contract-First implementation, and a system that allows you to change a contract without breaking an implementation. However, one question which has been repeatedly asked throughout our trainings and seminars is, “How do I Unit Test when I’m using the CODE Framework?” This article will examine the question and attempt to answer it.
Dependency Injection
In an MVVM or MVC solution, the answer to Low Coupling is to use some form of Dependency Injection (DI). There are two primary forms of DI (Figure 1) in use: Constructor Injection and Service Location.
Constructor Injection allows the application to pass a concrete implementation of an interface to a class constructor, which hooks the implementation up to an interface reference in the class.
As seen in this snippet, an interface is defined and implemented, as normal.
public interface IDemoInterface
{
string Name { get; set; }
void SayName();
}
public class DemoImplementation :
IDemoInterface
{
public string Name { get; set; }
public void SayName()
{
Console.WriteLine(Name);
}
}
Then, in Listing 1, the new class MyClass accepts the IDemoInterface implementation as a parameter in the constructor, and the method DoSayName is able to call a method in the implemented class.
Finally, in Listing 2, the DemoImplementation is instantiated, and passed to myClass via Constructor Injection.
Although this solution works in many situations, it’s difficult to implement when WCF or SOA-based applications are involved. WCF requires parameter-less constructors for many of its “automagic” functions, and Constructor Injection is an obvious roadblock to this requirement.
WCF requires parameter-less constructors for many of its “automagic” functions, and Constructor Injection is an obvious roadblock to this requirement.
Service Locator
A second solution for Dependency Injection is Service Location. In this model, an object is informed of the types of interfaces (Services) available to the application, and where the implementations should be located (Location). This pattern is ideal for WCF, SOA applications, and other formats requiring parameter-less constructors.
Listing 3 shows a Service Contract defined using a Response\Request pattern (described in the March\April 2012 issue of CODE Magazine).
Listing 4 shows the implementation of one method - the GetCustomerList method.
Finally, Listing 5 shows the method being called via the use of ServiceClient.Call<ICustomerService>.
This is where things get interesting! ServiceClient, in the CODE Framework, is a Service Locator. Therefore, something, somewhere, has to tell ServiceClient what to use as an implementation. Fortunately, CODE Framework provides a tool for doing just that! The Development Host (or Service Host, if you’re ready for production) allows you to populate your “Service Garden” with service implementations for each contract. In this sample application, I want the CustomerService to run on several bindings, so as seen in Listing 6, you add it to the Service Host in a variety of ways.
None of this should come as a surprise to anyone who has read through Markus Egger’s prior articles on Service-Oriented Application Development. What might come as a surprise is that this isn’t the only way to do the same thing!
One accusation frequently leveled against Service Locators is their difficulty to test. In his 2004 article, Martin Fowler points out that a decent Service Location Framework should make it easy to load up Mock Objects, and therefore make it easy to test your application without using the Service. CODE Framework provides the ServiceGardenLocal object as the mechanism for In-Process Testing and Service Hosting.
Why the ServiceGarden?
You may be wondering why you can’t simply test methods on your view models. Why do you need to bother with this whole ServiceGardenLocal thing? Take a look at the LoadData method in Listing 5. If you try to write a specification against that method when it resides on a ViewModel, it would cause problems. For instance, take the Specification in Listing 7.
The obvious problem with this Specification is that if the database ever changes, the list may contain more than three records, and the first record may not be EPS Software. In an Integration Test, you can write DB Scripts that specifically generate a set of data for testing, but in most Unit Testing scenarios, the developer only wants to test a specific “Unit” of code, in this case, the “LoadData” method. In this case, the developer is ensuring that once the data is retrieved from the service, it is populated into the correct property in the ViewModel (i.e., “Customers”). Integration Tests would be superfluous, and distracting. This is where Mock Objects are helpful!
Notice in Figure 2, the highlighted project - MockImplementation. This will be used for creating Mock Service Implementations for testing purposes. Listing 8 shows a newly created MockCustomerService that you can use in the Testing Project for bypassing the service.
But having a MockCustomerService isn’t enough. Now, the question is raised of how to connect the MockImplementation into the ServiceGarden. If you attempt to run the test as is, you will get a missing port exception.
You can add a few lines to the App.Config file in the Test Project.
<configuration>
<appSettings>
<add key="ServiceProtocol"
value="InProcess" />
</appSettings>
</configuration>
Then, in the CustomerTests class, you can add a SetupServiceGarden method, which checks the ServiceGardenLocal object for instances of ICustomerService, and if it needs an implementation, it adds MockCustomerService into the ServiceGardenLocal object. This way, when ServiceClient attempts to load ICustomerService, it uses the Local Process (due to the App.Config change), and it finds the Mock object you specified (due to the method you used to load the ServiceGardenLocal). See Listing 9 for the final test class.
HAPPY TESTING!