- Set expectations,
- Perform test and record actuals, and
- Evaluate actuals against expectations
Thursday, May 13, 2010
Testing, anatomy of a test
Hm, just firing off a quick post here - the basic structure of a test
Labels:
design,
fundamental,
test
Wednesday, April 7, 2010
Unit testing, how do I unit test WebService client X?
Another scenario I commonly encounter is the client side component to a WebService-based design.
For example,
which is typical of a thick WPF client. Now, first thing to note is that the question
Similarly then, the solution to this problem is to isolate our business from our remote invocation,
When it comes to implementing IProfileService, we have one of two choices. If we are really lazy, and have access to the WebReference source,
This has the advantage of no additional overhead or extraneous source files. The down-side is that maintenance is at a premium if the WebReference ever changes - which happens quite often in development. We must always remember to locate and modify the auto-generated classes with this interface definition!
Now, an alternative may be to automate the modification process, however that sounds like a lot of work to me. I would rather go the other route, and again, abstract the implementations a little.
Consider then,
Whichever route you go with, you have successfully isolated business from our remote service. This has the added advantage of being able to swap out ProfileServiceProxy with any implementation we like, say a WCF client, or a local instance of the actual service!
Our unit tests also benefit,
For example,
// a client side view, displaying
// server side profile data
public partial class ProfileView : UserControl
{
private readonly Session _session = null;
public ProfileViewModel ViewModel
{
get { return (ProfileViewModel)(DataContext); }
set { DataContext = value; }
}
public ProfileView ()
{
InitializeComponent ();
}
// command implementation, fetches and assigns server side
// data to client side data model
public void Command_RefreshProfile ()
{
// embedded transmission codes! tsk tsk tsk
//
// 1. get profile service
ProfileWebService service = new ProfileWebService ();
// 2. get profile
Profile profile = service.GetProfile (_session);
// 3. map to client side model
ViewModel = new ProfileViewModel (profile);
}
}
which is typical of a thick WPF client. Now, first thing to note is that the question
how do I unit test WebService client X?is a bit of a misnomer. We are not actually interested in unit testing our WebService client, or in this case an instance of ProfileWebService. As with our WebService example what we really want to do is test our business logic and only our business logic.
Similarly then, the solution to this problem is to isolate our business from our remote invocation,
// a formal contract, defining all public operations
// available for profile service
public class IProfileService
{
Profile GetProfile (Session session);
}
public partial class ProfileView : UserControl
{
private readonly Session _session = null;
private readonly IProfileService _service = null;
public ProfileViewModel ViewModel { get; set; }
// we now pass in a reference to an implementation of our
// profile service contract
public ProfileView (IProfileService service)
{
InitializeComponent ();
_service = service;
}
public void Command_RefreshProfile ()
{
// no embedded transmission codes! ah, nice clean code
Profile profile = _service.GetProfile (_session);
ViewModel = new ProfileViewModel (profile);
}
}
When it comes to implementing IProfileService, we have one of two choices. If we are really lazy, and have access to the WebReference source,
// auto-generated web reference. except for these
// comments. and IProfileService definition below.
public partial class ProfileWebService :
System.Web.Services.Protocols.SoapHttpClientProtocol,
IProfileService
{
// ...
}
This has the advantage of no additional overhead or extraneous source files. The down-side is that maintenance is at a premium if the WebReference ever changes - which happens quite often in development. We must always remember to locate and modify the auto-generated classes with this interface definition!
Now, an alternative may be to automate the modification process, however that sounds like a lot of work to me. I would rather go the other route, and again, abstract the implementations a little.
Consider then,
// a thin wrapper for our auto-generated classes.
// has advantage of referencing most current web
// reference when updated, without breaking existing
// consumers
public class ProfileServiceProxy : IProfileService
{
#region IProfileService Members
// very simple pass through to actual auto-generated
// implementation
public Profile GetProfile (Session session)
{
// it is very important to keep this bit clean,
// !!! NO BUSINESS LOGIC !!!
ProfileWebService service = new ProfileWebService ();
return service.GetProfile (session);
}
#endregion
}
Whichever route you go with, you have successfully isolated business from our remote service. This has the added advantage of being able to swap out ProfileServiceProxy with any implementation we like, say a WCF client, or a local instance of the actual service!
Our unit tests also benefit,
[TestMethod]
public void Test_Command_RefreshProfile ()
{
Session session = new Session ();
IProfileService mockService = null;
// instantiate mock with expectations
ProfileView view = new ProfileView (mockService);
view.Command_RefreshProfile ();
// verify results
}
Unit testing, how do I unit test WebService X with dependency Y?
Before moving on, I would like to build on our WebService example. In that example, we separated our business logic from our hosting solution. However, it may occur that our source solution has dependencies that must be fulfilled.
Consider this modified example,
Note the dependency introduced by member variable Context. If we were to perform our simple refactor, we introduce compile-time issues,
Fortunately, the solution is fairly straightforward. The key is realising our business logic of authenticate, fetch, instantiate is completely separate from the services that facilitate it. Our business class is a consumer of these services. From this perspective, it seems obvious to request these services as part of our invocation.
and our WebService now looks like
Again, our business logic does not care who or how a service is implemented, only that whomever is doing it, conforms to some known invocation. In this scenario, that an object instance of type AuthenticationService contains a method IsAuthenticated.
Our unit tests may look like
Consider this modified example,
// a profile web service, represents business logic
// hosted from a web service
public class ProfileWebSevice : System.Web.Services.WebService
{
// gets a profile for remote client
[WebMethod]
public Profile GetProfile (Session session)
{
Profile profile = null;
// obtain authentication service from
// web service context
AuthenticationService auth =
(AuthenticationService)(Context.Cache["auth"]);
// embedded business logic, bad bad bad
//
// 1. authenticate
if (auth.IsAuthenticated (session))
{
// 2. create sql connection
// 3. get profile
// 4. populate profile
profile = new Profile (dataReader);
}
return profile;
}
}Note the dependency introduced by member variable Context. If we were to perform our simple refactor, we introduce compile-time issues,
public class ProfileService
{
public Profile GetProfile (Session session)
{
// COMPILE-ERROR: "Context" does not exist here! oh nos!!!
AuthenticationService auth =
(AuthenticationService)(Context.Cache["auth"]);
// ...
}
}Fortunately, the solution is fairly straightforward. The key is realising our business logic of authenticate, fetch, instantiate is completely separate from the services that facilitate it. Our business class is a consumer of these services. From this perspective, it seems obvious to request these services as part of our invocation.
public class ProfileService
{
public Profile GetProfile (AuthenticationService auth, Session session)
{
if (auth.IsAuthenticated (session)) { ... }
// ...
}
}and our WebService now looks like
public class ProfileWebSevice : System.Web.Services.WebService
{
[WebMethod]
public Profile GetProfile (Session session)
{
AuthenticationService auth =
(AuthenticationService)(Context.Cache["auth"]);
ProfileService service = new ProfileService ();
Profile profile = service.GetProfile (auth, session);
return profile;
}
}
Again, our business logic does not care who or how a service is implemented, only that whomever is doing it, conforms to some known invocation. In this scenario, that an object instance of type AuthenticationService contains a method IsAuthenticated.
Our unit tests may look like
// test business logic without web service! yay!
[TestMethod]
public void Test_GetProfile_NullSession ()
{
AuthenticationService auth = new AuthenticationService ();
ProfileService service = new ProfileService ();
Profile actual = service.GetProfile (auth, null);
// verify profile expectations
// verify authentication service expectations
}
Thursday, April 1, 2010
Friday, March 19, 2010
Unit testing, how do I unit test WebService X?
Most of this article appears in a StackOverflow post I submitted some time ago.
As the first bit of meat we cover in this series, we have a fairly typical scenario.
For example
I have worked on a number of web-based systems that employ this pattern, or something very similar. The four steps performed in sequence represent a very specific business flow, and it is not at all unreasonable to expect this flow to be tested.
Unfortunately, our example does not lend itself to testing very easily. For one, anyone reviewing this source would have difficulty separating our business from service hosting. While this distinction may seem trivial, it is often a source of great confusion. Do we need to host ProfileWebService? What about client-side proxies? Should we invoke from a web-client?
In short, the answer is no. WebProfileService and GetProfile represent parts of another tier altogether, that of web service hosting - and as far as testing is concerned, we are completely uninterested in testing [what is essentially] a third party hosting solution. Ultimately, we are interested in exercising our business logic and only our business logic.
What we really want is something like,
which is completely free of any web service tom-foolery. Our web service then looks like,
Finally, to test our re-imagined service,
Making your WebMethods simple passthroughs to proper underlying business classes and removing logic from them completely, allows you to target "real" user code as opposed to the plumbing of your typical WebService implementation.
As the first bit of meat we cover in this series, we have a fairly typical scenario.
How do I unit test WebService X?This scenario often presents itself as a WebService with embedded business logic within it.
For example
// a profile web service, represents business logic
// hosted from a web service
public class ProfileWebSevice : System.Web.Services.WebService
{
// gets a profile for remote client
[WebMethod]
public Profile GetProfile (Session session)
{
Profile profile = null;
// embedded business logic, bad bad bad
//
// 1. authenticate
AuthenticationService auth = new AuthenticationService ();
if (auth.IsAuthenticated (session))
{
// 2. create sql connection
// 3. get profile
// 4. populate profile
profile = new Profile (dataReader);
}
return profile;
}
}
I have worked on a number of web-based systems that employ this pattern, or something very similar. The four steps performed in sequence represent a very specific business flow, and it is not at all unreasonable to expect this flow to be tested.
Unfortunately, our example does not lend itself to testing very easily. For one, anyone reviewing this source would have difficulty separating our business from service hosting. While this distinction may seem trivial, it is often a source of great confusion. Do we need to host ProfileWebService? What about client-side proxies? Should we invoke from a web-client?
In short, the answer is no. WebProfileService and GetProfile represent parts of another tier altogether, that of web service hosting - and as far as testing is concerned, we are completely uninterested in testing [what is essentially] a third party hosting solution. Ultimately, we are interested in exercising our business logic and only our business logic.
What we really want is something like,
// a profile service without web service hosting,
public class ProfileService
{
// gets a profile
public Profile GetProfile (Session session)
{
Profile profile = null;
// 1. authenticate
AuthenticationService auth = new AuthenticationService ();
if (auth.IsAuthenticated (session))
{
// 2. create sql connection
// 3. get profile
// 4. populate profile
profile = new Profile (dataReader);
}
return profile;
}
}which is completely free of any web service tom-foolery. Our web service then looks like,
// this web service is now a consumer of a business class,
// no embedded logic, so does not require direct testing
public class ProfileWebSevice : System.Web.Services.WebService
{
[WebMethod]
public Profile GetProfile (Session session)
{
ProfileService service = new ProfileService ();
Profile profile = service.GetProfile (session);
return profile;
}
}
Finally, to test our re-imagined service,
// test business logic without web service! yay!
[TestMethod]
public void Test_GetProfile_NullSession ()
{
ProfileService service = new ProfileService ();
Profile actual = service.GetProfile (null);
// verify results
}
Making your WebMethods simple passthroughs to proper underlying business classes and removing logic from them completely, allows you to target "real" user code as opposed to the plumbing of your typical WebService implementation.
Code fragment
Hello all, just testing new code formatting
public class SomeClass : ISomeInterface
{
public SomeClass ( ) { }
// interfaces
#region ISomeInterface Members
public void SomeWork ()
{
}
#endregion
}
Labels:
code format test
Thursday, February 18, 2010
Unit testing, better design
Ahem, so before we dig into some code, I would like to take a minute and say something.
I can be a very stubborn and obstinate person, and I am also incredibly lazy! As such, even having attended many lectures and throwing thousands and thousands of dollars at some of the finest academia, I ignored many of the simple, basic, fundamental principles of Computing they tried to instill in me.
Principles like
I am positive there are more, but these three form a triad of sorts, themes in my most recent adventures.
This sad state of ignorance was further compounded by youth, inexperience, and entering the "real world", where constraints prohibited my ability to exercise these principles.
Many, many, many years later, after much trial, error, and tribulation I have entered something of a personal [and professional!] renaissance. Rediscovering some of these principles, I am continually finding new ways to simplify, extend, and robustify [alright, that is not really a word, but whatever] my work.
If that were not enough, then may I also add: Applying these simple yet powerful principles facilitates unit testing! Yes! That is right! Not only will you produce better work, but you will be able to quantitatively and qualitatively prove it is better.
If you are not yet sold, I do ask that you continue reading. Certainly, for the attentive reader, you will see these three principles come up time and time again in the [real world!] examples I present.
As I have said, it has taken me a long time to come around and see this for myself. It is my hope here and now, that you benefit from my experience: When designing a functional component, think about how Interfaces, Encapsulation, and SoC can help you. Invariably, such considerations will lead to better design and better product.
I can be a very stubborn and obstinate person, and I am also incredibly lazy! As such, even having attended many lectures and throwing thousands and thousands of dollars at some of the finest academia, I ignored many of the simple, basic, fundamental principles of Computing they tried to instill in me.
Principles like
I am positive there are more, but these three form a triad of sorts, themes in my most recent adventures.
This sad state of ignorance was further compounded by youth, inexperience, and entering the "real world", where constraints prohibited my ability to exercise these principles.
Many, many, many years later, after much trial, error, and tribulation I have entered something of a personal [and professional!] renaissance. Rediscovering some of these principles, I am continually finding new ways to simplify, extend, and robustify [alright, that is not really a word, but whatever] my work.
If that were not enough, then may I also add: Applying these simple yet powerful principles facilitates unit testing! Yes! That is right! Not only will you produce better work, but you will be able to quantitatively and qualitatively prove it is better.
If you are not yet sold, I do ask that you continue reading. Certainly, for the attentive reader, you will see these three principles come up time and time again in the [real world!] examples I present.
As I have said, it has taken me a long time to come around and see this for myself. It is my hope here and now, that you benefit from my experience: When designing a functional component, think about how Interfaces, Encapsulation, and SoC can help you. Invariably, such considerations will lead to better design and better product.
Subscribe to:
Posts (Atom)
