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
}