I often describe DoInstead as the 10lb/kg hammer to change behavior – if all fails, and any other API doesn’t help use it.
I want to give you a couple of examples on different occasions where using DoInstead can be helpful, above and beyond the call of duty. Let’s start with the class we’ll work on. This class is called UserManager and handles log-ins.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class UserManager { string prefix = "pre"; string suffix = "suff"; public bool CanUserLogIn(string user) { return Authenticate(prefix, user, suffix); } private bool Authenticate(string prefix, string user, string suffix) { string fullUserName = prefix + user + suffix; return Authenticate(fullUserName); } private bool Authenticate(string fullUserName) { bool result = Authenticator.IsUserAuthorized(fullUserName); return result; } } |
As you can see, it has an overloaded Authenticate private method, both called by the public method.
For our first test, I’d like to call the real Authenticate method with the 3 arguments, but fake the internal with the single argument. Not that if the methods were’ public, this wasn’t a real issue, since the public API lets you specify the exact overload. With non-public methods, we need to get creative.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[TestMethod] public void CanUserLogIn_2ndAuthenticateCallFaked() { UserManager manager = new UserManager(); Isolate.NonPublic.WhenCalled(manager, "Authenticate").DoInstead( callContext => { if (callContext.Parameters.Length == 3) { callContext.WillCallOriginal(); return true; } return false; }); Assert.IsFalse(manager.CanUserLogIn("Invalid")); } |
Since we’re dealing with non-public methods here, so I’m using the Isolate.NonPublic APIs. In the DoInstead clause, we can check the number of parameters. If the number of parameters is 3, we’ll call the original method. Otherwise, we’ll just return a fake false value.
What’s that WillCallOriginal thingy? It’s a new API we’ve added in Isolator version 7.3. It allows you to specify that once the DoInstead code runs, it will continue to run the original method. The return statement that follows is required for syntactic reasons, and in our case doesn’t really change anything. Don’t believe me? Debug it!
Ok, don’t. See if I care.
Let me show you another test to show that it actually works:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[TestMethod] public void CanUserLogIn_BothCallsAreReal_LoggingBefore() { UserManager manager = new UserManager(); Isolate.NonPublic.WhenCalled(manager, "Authenticate").DoInstead( callContext => { if (callContext.Parameters.Length == 3) { Console.WriteLine(@"Prefix: {0}, Suffix: {1}: , User: {2}", callContext.Parameters[0], callContext.Parameters[2], callContext.Parameters[1]); } if (callContext.Parameters.Length == 1) { Console.WriteLine(@"Full Name: {0}", callContext.Parameters[0]); } callContext.WillCallOriginal(); return true; }); Assert.IsTrue(manager.CanUserLogIn("Valid")); } |
This time, we’re not faking anything. But we’ve added some logging just before the actual methods run. It’s a different method of logging since each overload looks different. Using DoInstead here doesn’t have any impact on the test, but it helps if you need some logging magic.
It’s a bit like AOP, isn’t it? Injecting code before the actual method runs.
But what if we need to inject code after the method runs?
Wait for the next post to find out…