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 }