Showing posts with label plugin. Show all posts
Showing posts with label plugin. Show all posts

Tuesday, July 29, 2008

TDDing a CRM 4.0 Plugin - Part 7

Well, hopefully that goes over all the basics of TDDing a CRM 4.0 Plugin.

After the first post I stopped doing some refactoring. There are also a LOT of tests cases not hit here. I'm not going to code them all out, but I will at least show you the resulting refactored code:


using System;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using Microsoft.Crm.Sdk;
using NMock2;
using NMock2.Monitoring;
using NUnit.Framework;

namespace TDD_with_CRM_Plugin
{
[TestFixture]
public class PluginTestCases
{
#region Setup/Teardown

[SetUp] // executes at the beginning of each test case. Executed only once per TextFixture
public void Setup()
{
mock = new Mockery();
webService = mock.NewMock();
context = mock.NewMock();
crmService = mock.NewMock();
correlationID = Guid.NewGuid();
}

#endregion

private Mockery mock;
private IWebServiceWrapper webService;
private IPluginExecutionContext context;
private Guid correlationID;
private string resultingUrl = "http://someurl";
private ICrmService crmService;

private void SetupExpectationThatCreateCrmServiceIsCalled()
{
Expect.Once.On(context).Method("CreateCrmService").With(true).Will(Return.Value(crmService));
}

private void SetupExpectationThatCRMWebServiceWillBeCalledWithUpdatedDynamicEntity(
DynamicEntity dynamicEntityWithURL)
{
Expect.Once.On(crmService).Method("Update").With(new DynamicEntityMatcher(dynamicEntityWithURL)).Will(
new PluginRecursionAction(context));
}

private DynamicEntity CreateAndSetupExpectationsThatDynamicEntityIsUpdatedWithURLFromWebService(
DynamicEntity account)
{
DynamicEntity dynamicEntityWithURL = CloneDynamicEntity(account);
dynamicEntityWithURL["new_sharepointurl"] = resultingUrl;
return dynamicEntityWithURL;
}

private void SetupExpectationThatCustomWebServiceIsCalledWithAccount(DynamicEntity account)
{
Expect.Once.On(webService).Method("UpdateAccount").With(account).Will(Return.Value(resultingUrl));
}

private void SetupExpectationsThatCorrelationIDWillBeRead()
{
Expect.Once.On(context).GetProperty("CorrelationId").Will(Return.Value(correlationID));
}

public DynamicEntity GetSampleAccount()
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof (DynamicEntity));
return
(DynamicEntity)
xmlSerializer.Deserialize(new StreamReader(@"Sample Files\account.sample.xml").BaseStream);
}

private DynamicEntity CloneDynamicEntity(DynamicEntity account)
{
if (account == null)
throw new ArgumentNullException("account", "The supplied DynamicEntity cannot be null.");
if (String.IsNullOrEmpty(account.Name))
throw new ArgumentOutOfRangeException("account",
"The name of the DynamicEntity can not be null or blank.");
XmlSerializer xmlSerializer = new XmlSerializer(typeof (DynamicEntity));
MemoryStream memoryStream = new MemoryStream();
xmlSerializer.Serialize(memoryStream, account);
memoryStream.Seek(0, SeekOrigin.Begin);
DynamicEntity clonedDynamicEntity = (DynamicEntity) xmlSerializer.Deserialize(memoryStream);

return clonedDynamicEntity;
}

private DemoPlugin GetDependencyInjectedPlugin()
{
return new DemoPlugin(webService);
}

private void SetupExpectationsThatPostEntityImagePropertyBagIsRead(PropertyBag postEntityImages)
{
Expect.Once.On(context).GetProperty("PostEntityImages").Will(Return.Value(postEntityImages));
}

private PropertyBag CreatePropertyBagWithAccount(DynamicEntity account)
{
PropertyBag postEntityImages = new PropertyBag();
postEntityImages["postImage"] = account;
return postEntityImages;
}

private DynamicEntity CreateSampleAccountDEWithPopulatedValues()
{
// the account entity that fired the update
DynamicEntity account = new DynamicEntity("account");
account["accountid"] = new Key(Guid.NewGuid());
account["accountnumber"] = "someAccountNumber";
account["name"] = "accountName";
return account;
}

[Test]
public void Execute_HappyPathExecutesAsExpected()
{
DemoPlugin plugin = GetDependencyInjectedPlugin();

SetupExpectationsThatCorrelationIDWillBeRead();

DynamicEntity account = CreateSampleAccountDEWithPopulatedValues();
PropertyBag postEntityImages = CreatePropertyBagWithAccount(account);

SetupExpectationsThatPostEntityImagePropertyBagIsRead(postEntityImages);
SetupExpectationThatCustomWebServiceIsCalledWithAccount(account);
SetupExpectationThatCreateCrmServiceIsCalled();

DynamicEntity dynamicEntityWithURL =
CreateAndSetupExpectationsThatDynamicEntityIsUpdatedWithURLFromWebService(account);

SetupExpectationsThatCorrelationIDWillBeRead();
SetupExpectationThatCRMWebServiceWillBeCalledWithUpdatedDynamicEntity(dynamicEntityWithURL);

plugin.Execute(context);
mock.VerifyAllExpectationsHaveBeenMet();
}

[Test]
public void New_DefaultDependencyIsCreatedCorrectly()
{
DemoPlugin plugin = new DemoPlugin();
Assert.IsInstanceOfType(typeof (MyWebService), plugin.webService);
}

[Test]
public void New_DependencyInjectionTakes()
{
DemoPlugin plugin = new DemoPlugin(webService);
Assert.AreEqual(webService, plugin.webService);
}
}

public class DynamicEntityMatcher : Matcher
{
private readonly DynamicEntity leftSide;

public DynamicEntityMatcher(DynamicEntity leftSide)
{
this.leftSide = leftSide;
}

public override bool Matches(object o)
{
if (!(o is DynamicEntity))
return false;

DynamicEntity rightSide = (DynamicEntity) o;
if (leftSide.Name != rightSide.Name)
return false;

foreach (Property property in leftSide.Properties)
{
if (!(rightSide.Properties.Contains(property.Name)))
return false;
if (!(PropertiesAreEqual(leftSide[property.Name], rightSide[property.Name])))
return false;
}
return true;
}

private bool PropertiesAreEqual(object leftSideProperty,
object rightSideProperty)
{
if (leftSideProperty == null && rightSideProperty == null)
return true;
if (leftSideProperty == null)
return false;

if (leftSideProperty.GetType() != rightSideProperty.GetType())
return false;
if (leftSideProperty.GetType() == typeof (string))
return (string) leftSideProperty == (string) rightSideProperty;

PropertyInfo[] properties =
leftSideProperty.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo property in properties)
{
object leftValue = property.GetValue(leftSideProperty, null);
object rightValue = property.GetValue(rightSideProperty, null);
if (leftValue == null && rightValue != null)
return false;
if (leftValue != null && !leftValue.Equals(rightValue))
return false;
}
return true;
}

public override void DescribeTo(TextWriter writer)
{
writer.Write("DynamicEntities are equal");
}
}

public class PluginRecursionAction : IAction
{
private readonly IPluginExecutionContext context;

public PluginRecursionAction(IPluginExecutionContext context)
{
this.context = context;
}

#region IAction Members

public void Invoke(Invocation invocation)
{
new DemoPlugin().Execute(context);
}

public void DescribeTo(TextWriter writer)
{
writer.Write("Plugin will recurse");
}

#endregion
}
}


And the actual plugin class, refactored into it's own file:


using System;
using Microsoft.Crm.Sdk;

namespace TDD_with_CRM_Plugin
{
public class DemoPlugin : IPlugin
{
private static Guid correlationID;
internal IWebServiceWrapper webService;

public DemoPlugin() : this(new MyWebService())
{
}

public DemoPlugin(IWebServiceWrapper webService)
{
this.webService = webService;
}

#region IPlugin Members

public void Execute(IPluginExecutionContext context)
{
if (IsRecursiveCall(context))
return;
DynamicEntity account = (DynamicEntity) context.PostEntityImages["postImage"];
string resultingUrl = webService.UpdateAccount(account);
ICrmService crmService = context.CreateCrmService(true);
account["new_sharepointurl"] = resultingUrl;
crmService.Update(account);
ClearCorrelationID();
}

#endregion

private void ClearCorrelationID()
{
correlationID = Guid.Empty;
}

private bool IsRecursiveCall(IPluginExecutionContext context)
{
if (correlationID == Guid.Empty)
correlationID = context.CorrelationId;
else if (correlationID == context.CorrelationId)
return true;
return false;
}
}
}


And the IWebServiceWrapper interface and implementation put into their own class. Notice here that we never tested this class. We don't care about it's functionality with these tests. We just care that it's called.


using Microsoft.Crm.Sdk;

namespace TDD_with_CRM_Plugin
{

public interface IWebServiceWrapper
{
string UpdateAccount(DynamicEntity account);
}

public class MyWebService : IWebServiceWrapper
{
#region IWebServiceWrapper Members

public string UpdateAccount(DynamicEntity account)
{
return "";
}

#endregion
}
}



You'll probably notice rather quickly that there is a lot more code in our test than our actual plugin. Don't be turned off by this. It just means you are thoroughly testing your code. If you make a change to your plugin you can quickly verify it all functions as like you expected without having to deploy and run through a full regression test suite through the CRM UI, which can be significanly more painful than this up-front coding.

I hope this was useful to someone. If you have any questions or comments, please let me know.

=-}

TDDing a CRM 4.0 Plugin - Part 6

One thing that I haven't touched on so far is the testing of a read from the CRM WebService. We've sent it a record to update, but we haven't read from it yet.

There are a few different approaches you can take to this:



  1. Use a plugin that serializes the DynamicEntity in the Image PropertyBags to write to a file. You can then deserialize that file in test cases and use that as your sample objects. The downside to this is that you can only do this for DynamicEntity classes since that's all that is available in the plugins. If you are working with "core" objects, this does you no good.


  2. Create your own sample objects from scratch. This can be very tedious (less so if you're working with core objects, which I don't recommend) and prone to errors as it's likely you'll use the wrong Property type with the wrong attribute (LookupProperty with the "owner" attribute).


  3. Use an Object Builder/Mother to create random objects at runtime. The problem here is that we're talking "random" and that's usually not good unless you want to put significant coding behind making them "smart random". In which case, why not just write the samples from scratch.




The #1 approach is my favorite as it makes for less coding and more realiable objects. However, it means that anytime you want to retrieve objects from the CRM WebService you can't use the standard Retrieve and RetrieveMultiple commands as they return "core" objects by default.

#2 is pretty straight forward and we've already done it before:


DynamicEntity account = new DynamicEntity("account");
account["accountid"] = new Key(Guid.NewGuid());
account["accountnumber"] = "someAccountNumber";
account["name"] = "accountName";


#3, well, I don't even want to get into that. Have fun with it though. =-}

Creating a plugin that will serialize your objects is pretty easy:


public class SerializeEntityPlugin : IPlugin
{
public void Execute(IPluginExecutionContext context)
{
DynamicEntity entity = (DynamicEntity) context.PostEntityImages["PostImage"];
XmlSerializer xmlSerializer = new XmlSerializer(typeof(DynamicEntity));
xmlSerializer.Serialize(new StreamWriter(@"c:\temp\" + GetFilename(entity), false), entity);

}

private string GetFilename(DynamicEntity entity)
{
foreach (Property property in entity.Properties)
{
if (property.GetType() == typeof(KeyProperty))
{
return entity.Name + "--" + ((KeyProperty) property).Value.Value + ".xml";
}
}
string returnValue = entity.Name + "--Unknown Primary Key.xml";
return returnValue;
}
}



Register this against an account and it's update event and then update an account through the UI (make sure the "C:\temp" directory exists). You should get a file called "account--.xml".


Now we've got a serialized DynamicEntity object. We can deserialize this object and use it in our tests. To do that, first copy the resulting file to your solution. Make sure you setup the file to be copied to your output directory so that it's easily available during the runtime of the tests (I would also suggest renaming the file to not include the GUID so that you don't have to worry about it when referencing the filename).


public DynamicEntity GetSampleAccount()
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof (DynamicEntity));
return (DynamicEntity) xmlSerializer.Deserialize(new StreamReader(@"Sample Files\account.sample.xml").BaseStream) ;
}


You could also refactor this out or create a wrapping method to get a variety of different objects for your testing.

In the end, I recommend both techniques (not the 3rd one). You can get a long way with just creating your own objects but if your code depends on all the various CRM value types (Picklist, Lookup, CrmDateTime), then it can be pretty tedious to create the testing objects, whereas this is pretty simple if you don't mind a little plugin deployment.

=-}

TDDing a CRM 4.0 Plugin - Part 5

At the end of Part 4 I mentioned a critical flaw with the current code of our plugin. It will naturally recurse and cause a stack overflow. This is because the plugin that triggers off of the updating of an account also updates that account.

There are a few different ways you can prevent plugin recursion from happening. There are numerous sources that you can google for about how to do this. I'll be using the CorrelationID in the plugin context to catch this.

However, we're more curious about how to test this to make sure it doesn't happen in our production code.

At this point we have to get into specifics of the NMock library. While I'll discuss and show you the specific implementation of the test, I'll go over enough of the concepts that you should be able to use nearly any mocking framework to do the same.

First, let's review the test:


Expect.Once.On(crmService).Method("Update").With(new DynamicEntityMatcher(dynamicEntityWithURL));


Here we're telling the NMock framework that we "expect" that the crmService will be called with a specific instance of the DynamicEntity object (which is checked by our special DynamicEntityMatcher class we created during the last post).

We're going to modify this to tell NMock that when this happens, we expect something else to occur as a result. If we look at the definition of the With method we see it returns an IMatchSyntax interface. One of the methods on that interface is the Will method. The Will method takes an IAction interface. We've used an instance of this interface before:


Expect.Once.On(context).Method("CreateCrmService").With(true).Will(Return.Value(crmService));


Here our IAction is the result of the Return.Value() method and we're saying our method call (CreateCrmService) will return a specific value (crmService). So we just need to create a new IAction:


public class PluginRecursionAction : IAction
{
private readonly IPluginExecutionContext context;

public PluginRecursionAction(IPluginExecutionContext context)
{
this.context = context;
}

public void Invoke(Invocation invocation)
{
new DemoPlugin().Execute(context);
}

public void DescribeTo(TextWriter writer)
{
writer.Write("Plugin will recurse");
}
}


And then update our previous Expect to use this action:


Expect.Once.On(crmService).Method("Update").With(new DynamicEntityMatcher(dynamicEntityWithURL)).Will(new PluginRecursionAction(context));


Now if we run our test case we'll see something we saw a while back:

NMock2.Internal.ExpectationException: unexpected invocation of pluginExecutionContext.PostEntityImages

Knowing our code, this means it's going through and trying to run a second time. Success!

Let's now make our code a little smarter. Since we're going to use the CorrelationID on the context to determine if we're in a recursive call we need to setup the expectation that it will be read from our mock object:


Guid correlationID = Guid.NewGuid();
Expect.Once.On(context).GetProperty("CorrelationId").Will(Return.Value(correlationID));



In fact, we expect that it will be read twice. Once for the initial call and a second time for the result of our custom IAction. So our final code:


Guid correlationID = Guid.NewGuid();
Expect.Once.On(context).GetProperty("CorrelationId").Will(Return.Value(correlationID));
Expect.Once.On(context).GetProperty("CorrelationId").Will(Return.Value(correlationID));



We can run our test again, although it doesn't exactly provide useful information. It's a good habit though... You should see an unexpected call to the PostEntityImages property and two unexecuted expectations for the CorrelationID.

Now it's time to write the code. The basic principle is that I'll have a static variable in my plugin called "correlationID". I'll set this if it's empty to the current context's CorrelationID. If it's not empty, then I'll check to see if it matches the current context's CorrelationID. If it does, then I'm calling myself, if not, then it's something else.

Here is the final code I end up with. Review it at your own pace. Now, this is from a previous plugin I worked with. I just noticed that there is a case where if two updates are occuring at the same time or nearly the same time, it could be problematic. I need to work through some tests for this and see what happens. I would not take this code to be production worthy at this point. Again, I'm teaching TDD with CRM plugins here, not proper CRM plugin development. =-}


public void Execute(IPluginExecutionContext context)
{
if (IsRecursiveCall(context))
return;
DynamicEntity account = (DynamicEntity) context.PostEntityImages["postImage"];
string resultingUrl = webService.UpdateAccount(account);
ICrmService crmService = context.CreateCrmService(true);
account["new_sharepointurl"] = resultingUrl;
crmService.Update(account);
ClearCorrelationID();
}

private void ClearCorrelationID()
{
correlationID = Guid.Empty;
}

private bool IsRecursiveCall(IPluginExecutionContext context)
{
if (correlationID == Guid.Empty)
correlationID = context.CorrelationId;
else if (correlationID == context.CorrelationId)
return true;
return false;
}



Now with this code in place and I re-run the test, it passes. I'm reasonably happy at this point (with the caveat already mentioned) that this plugin should not recurse.

Stay tuned for the (possibly last) post in the series regarding how to mock the retrieval of an object from the CRM WebService.

=-}

TDDing a CRM 4.0 Plugin - Part 4

In Part 3, I showed you how to make the simple call to our external webservice. In this part, we'll expand upon that and show you how to make calls to the CRM WebService with information returned from the other webservice.

However, let's add some context to all of this. Let's say we've got a nicely integrated system between CRM and Sharepoint 2007 (MOSS). Our webservice that we've called in Part 3 takes the account information we supplied and determines if a Sharepoint site needs to be provisioned, and if so, creates that site and populates it with some data. Again, for simplicity I'm not considering all the various issues involved with the integration. We're making this plugin very simple. Assume that all the serious logic and code is all in the webservice that we aren't developing here.

This webservice is called and if a site is provisioned it returns a URL that we can store in CRM and provide as a link to the user so they can easily access the Sharepoint site. For our example here we'll assume the site is provisioned and a URL is returned. We now need to update CRM with that information.

First, let's expand our "Happy Path" test to have the Expect clause include a return value from the webservice call:


Expect.Once.On(webService).Method("UpdateAccount").With(account).Will(Return.Value("http://someurl"));


But of course, before this will work at all, we also need to update our method signature in our interface and the implementing class to have a return value:


public class MyWebService : IWebServiceWrapper
{
public string UpdateAccount(DynamicEntity account)
{
return "";
}
}

public interface IWebServiceWrapper
{
string UpdateAccount(DynamicEntity account);
}


At this point we could run our tests again, but there's no point except to see we didn't break the code (from compiling). The test will still pass because all of our assertions (including the "VerifyAllExpectations...") were met.

Now comes the fun part... we need to make sure the test verifies that the CRM webservice is called and sent the correct data.

But wait! We don't have a CRM WebService. Ah, but we do! It's in the plugin context. And, thankfully, it's an interface, so we can mock it easily:


[Test]
public void Execute_HappyPathExecutesAsExpected()
{
DemoPlugin plugin = GetDependencyInjectedPlugin();

DynamicEntity account = CreateSampleAccountDEWithPopulatedValues();
PropertyBag postEntityImages = CreatePropertyBagWithAccount(account);
SetupExpectationsThatPostEntityImagePropertyBagIsRead(postEntityImages);

Expect.Once.On(webService).Method("UpdateAccount").With(account).Will(Return.Value("http://someurl"));

ICrmService crmService = mock.NewMock();
Expect.Once.On(context).Method("CreateCrmService").With(true).Will(Return.Value(crmService));

plugin.Execute(context);
mock.VerifyAllExpectationsHaveBeenMet();

}


We've said that the "context" object will have the CreateCrmService method called with "true" passed in as a parameter and it will return the crmService object we have mocked.

Now comes the somewhat tricky part... we have this DynamicEntity object that represents our account. We want to update that to reflect the new website URL we got back from our custom webservice and then call the CRM webservice with that object. But remember that all of this code we're setting up is irrelevant of the actual execution of the code.

In other words, even though I've laid out my test code to represent what's happening in the production code, this has no meaning in terms of the actual execution. If I setup this DynamicEntity object, setup all the expects, and then later update a property on it to reflect the new URL, the first usages of the DynamicEntity object will have the website url in it. So, I have to create a second instance of the DynamicEntity object that will represent the object with an updated URL.

Let's get some code in front of you and perhaps this will make more sense:


[Test]
public void Execute_HappyPathExecutesAsExpected()
{
DemoPlugin plugin = GetDependencyInjectedPlugin();

DynamicEntity account = CreateSampleAccountDEWithPopulatedValues();
PropertyBag postEntityImages = CreatePropertyBagWithAccount(account);
SetupExpectationsThatPostEntityImagePropertyBagIsRead(postEntityImages);

string resultingUrl = "http://someurl";
Expect.Once.On(webService).Method("UpdateAccount").With(account).Will(Return.Value(resultingUrl));

ICrmService crmService = mock.NewMock();
Expect.Once.On(context).Method("CreateCrmService").With(true).Will(Return.Value(crmService));

DynamicEntity dynamicEntityWithURL = CloneDynamicEntity(account);
dynamicEntityWithURL["new_sharepointUrl"] = resultingUrl;

Expect.Once.On(crmService).Method("Update").With(dynamicEntityWithURL);

plugin.Execute(context);
mock.VerifyAllExpectationsHaveBeenMet();

}


So I've created a clone of the "account" object we used before and set the property on it with the url from our custom webservice. I've cheated here and used a CloneDynamicEntity method I developed for the ABetterCRM library. I then setup the expectation that it will be used in the Update method of our CRM WebService.

Note: I'm assuming here the Account entity in CRM has been expanded to include a string attribute called "new_sharepointUrl"

Run the test, and we get a failed test because not all expectations were called. Excellent. Let's write the production code now:


public void Execute(IPluginExecutionContext context)
{
DynamicEntity account = (DynamicEntity) context.PostEntityImages["postImage"];
string resultingUrl = webService.UpdateAccount(account);
ICrmService crmService = context.CreateCrmService(true);
account["new_sharepointurl"] = resultingUrl;
crmService.Update(account);
}


Excellent. Now, we run our test and... WHAT?! It failed?

"NMock2.Internal.ExpectationException: unexpected invocation of crmService.Update()
Expected:
1 time: crmService.Update(equal to ) [called 0 times]"

That's weird! We can double check out code 100 times and it looks right.

The trick here is how CRM's DynamicEntity object and how NMock work together. NMock will use the object's Equals method by default to determine if objects are equal. However, if you read earlier posts of mine you'll see that two DynamicEntity objects with the same values do not equate. So, how do we get around this?

There are a couple of options here. You could create a new class that inherits from DynamicEntity and properly overrides the Equals method (my preference). However, if you run across this for other objects then you'll have to keep overriding. If you ever run across a sealed class, you'll be stuck. Let's see if we can take advantage of the NMock library.

If we take a look at the NMock With method we see one option for the parameter is a Matcher class. So let's create our own version of this. Forgive me for not going into details here, but this technique will depend largely on the mocking library you use and each one has a different process for doing this. I don't want this to be too specific to NMock so I'll just give you the code for the matcher:


public class DynamicEntityMatcher: Matcher
{
private readonly DynamicEntity leftSide;

public DynamicEntityMatcher(DynamicEntity leftSide)
{
this.leftSide = leftSide;
}

public override bool Matches(object o)
{
if (!(o is DynamicEntity))
return false;

DynamicEntity rightSide = (DynamicEntity) o;
if (leftSide.Name != rightSide.Name)
return false;

foreach (Property property in leftSide.Properties)
{
if (!(rightSide.Properties.Contains(property.Name)))
return false;
if (!(PropertiesAreEqual(leftSide[property.Name], rightSide[property.Name])))
return false;
}
return true;
}

private bool PropertiesAreEqual(object leftSideProperty,
object rightSideProperty)
{
if (leftSideProperty == null && rightSideProperty == null)
return true;
if (leftSideProperty == null)
return false;

if (leftSideProperty.GetType() != rightSideProperty.GetType())
return false;
if (leftSideProperty.GetType() == typeof(string))
return (string)leftSideProperty == (string)rightSideProperty;

PropertyInfo[] properties = leftSideProperty.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo property in properties)
{
object leftValue = property.GetValue(leftSideProperty, null);
object rightValue = property.GetValue(rightSideProperty, null);
if (leftValue == null && rightValue != null)
return false;
if (leftValue != null && !leftValue.Equals(rightValue))
return false;
}
return true;
}

public override void DescribeTo(TextWriter writer)
{
writer.Write("DynamicEntities are equal");
}
}


Without going line by line, it first compares the names of the two DynamicEntities, then it compares each of the properties using reflection.

Now, with using this Matcher we get a passed test! Yay!

However, there is something here that is very wrong and will cause huge problems. Recursion. This plugin will be registered on the post-update event of the account object. We just performed an update of the account object. This plugin will cause itself to fire. If we don't do anything to catch this, we will get a stack overflow very quickly.

I will discuss how to handle this in Part 5.

=-}

TDDing a CRM 4.0 Plugin - Part 3

In Part 2 I showed you how to TDD the reading of the PostImage entity images from the plugin context. This part will go over how to mock the call to the webservice.

To review, at this point we have gotten the DynamicEntity object out of the plugin context. Now we need to do something useful with it. We will hand it off to a custom built webservice (this is not the CRM webservice) for external processing. There are many different ways to handle an integration like this. I'd prefer something a bit more fail-safe like having a Biztalk server receive the message and process it. In our case though, it couldn't matter less.

So let's start with our test case to make sure our webservice method is called. The first question I always have when starting a new test case is whether to create a new one, or expand on an old one. In this case, we're adding to the basic "happy path" of the plugin. We could create a new test case but it would largely contain all the same code as before. Moreover, as soon as I added in the additional code, I'd have to add the test code (like the Expect calls) to the first test case. In the end, I'd end up with two identical test cases. I don't want to get into the debates here as they're off-track from the discussion at hand. For simplicity, I'm going to add to the old test case.

I need to add a new "Expect" call to the test case to make sure the webservice is getting called. Since the intent of the test is changing, let's rename the method to:


public void Execute_HappyPathExecutesAsExpected()


I've already mocked the object out (IWebServiceWrapper webService) so no need to add additional code there. So all there is to do is add the new expect clause:


[Test]
public void Execute_HappyPathExecutesAsExpected()
{
DemoPlugin plugin = GetDependencyInjectedPlugin();

DynamicEntity account = CreateSampleAccountDEWithPopulatedValues();
PropertyBag postEntityImages = CreatePropertyBagWithAccount(account);
SetupExpectationsThatPostEntityImagePropertyBagIsRead(postEntityImages);

// new code
Expect.Once.On(webService).Method("UpdateAccount").With(account);
// end new code

plugin.Execute(context);
mock.VerifyAllExpectationsHaveBeenMet();

}


If I run the test, I can see it fails because our method called "UpdateAccount" doesn't exist yet. We should resolve this error first because the test case fails in validating the test case code, not because the production code isn't written yet. So, add the UpdateAccount method to the IWebServiceWrapper interface and the implementing class (so the project compiles):


public class MyWebService : IWebServiceWrapper
{
public void UpdateAccount(DynamicEntity account)
{

}
}

public interface IWebServiceWrapper
{
void UpdateAccount(DynamicEntity account);
}


Good, now when we run the test we see that not all of our expectations were met. Perfect! Now that we know our test is valid, we can write the code that makes the test pass:


public void Execute(IPluginExecutionContext context)
{
DynamicEntity account = (DynamicEntity) context.PostEntityImages["postImage"];
webService.UpdateAccount(account);
}


And when we run out test we can see it now passes!

We'll skip the refactoring this time around as there isn't much to do other then just throwing the new Expect call into it's own method.

Stay tuned for Part 4, where I'll show you how to simulate calls to the CRM WebService.

=-}

Wednesday, July 16, 2008

TDDing a CRM 4.0 Plugin - Part 2

In Part 1, I gave a quick introduction into the world of TDD. Nothing I did was specific to CRM. During this post, I'll talk more about specific issues with CRM Plugin development.


Let's start by taking a look at the existing code from Part 1:

public class DemoPlugin { }


Notice here that we haven't setup our plugin to implement the IPlugin interface that is required in a CRM plugin. Since we hadn't tested anything around this we hadn't written the production code yet.



Let's update our class to implement the IPlugin interface:


   
public void Execute(IPluginExecutionContext context)
{
throw new NotImplementedException();
}



We also need to start testing this code. But, what does this code do!? If you recall from Part 1, this code will fire when an account is created or updated and call a webservice, passing vital information to that webservice. We're considering vital information the account's Name, GUID and Account Number. For the sake of this example we don't care what the webservice does with this information since we're mocking out that interface.

So there are a few vital processes here:
  • Retrieve account information from the EntityImage passed in through the plugin context
  • Call the webservice with specific information
So now we need to start with writing out test case. The first thing we'll do is mock out the plugin context and tell NMock that we'll retrieve the EntityImage information from it.

I'm going to start here in skipping past a lot of the detail code analysis and things specific to TDD. I want to focus more on details regarding TDD with CRM Plugins. There are tons of good references out there and just a quick google search for them will provide tons of good examples.

One thing I will say is that I tend to setup my test cases in a specific format. The top portion sets up any mock objects. The next session sets up all expectations in order in which I expect them to occur in code. Next is the method call that I'm testing and then finally any assertions or, in this case, just verifying that all my expectations were met.

This is my test case expecting that the entity image will be read from the plugin context:


[Test]
public void Execute_EntityImageIsReadFromContext()
{
Mockery mock = new Mockery();
IWebServiceWrapper webService = mock.NewMock();
IPluginExecutionContext context = mock.NewMock();

DemoPlugin plugin = GetDependencyInjectedPlugin();

DynamicEntity account = new DynamicEntity("account");
account["accountid"] = new Key(Guid.NewGuid());
account["accountnumber"] = "someAccountNumber";
account["name"] = "accountName";

PropertyBag postEntityImages = new PropertyBag();
postEntityImages["postImage"] = account;

Expect.Once.On(context).GetProperty("PostEntityImages").Will(Return.Value(postEntityImages));

plugin.Execute(context);
mock.VerifyAllExpectationsHaveBeenMet();

}




I don't want to dwell into the details here because it's specific to the NMock and NUnit frameworks. But let's cover a few bigger points

First is I create a simple DynamicEntity object that will represent the account record as it's passed into the plugin context. Notice here that I'm putting values in. This will be part of the "happy path" tests that will be written. Later on I'll show you how to test for non-happy-paths.

Next, I create a PropertyBag object which will hold the account object created above.

Finally, I tell the NMock framework (that has mocked out the IPluginExecutionContext object) that I Expect the PostEntityImages property to be read. When it is, I want to return the PropertyBag object I created above.

I'll skip the boring screenshot showing that my test doesn't pass because all the invocations weren't happening.

No, if I just implement one line of code I get a passing test:



public void Execute(IPluginExecutionContext context)
{
DynamicEntity account = (DynamicEntity) context.PostEntityImages["postImage"];
}



So that sure seemed like a lot of work in writing that test case just for just one line of code. I beg you not to look at it like this even though such an observation is inevitable. This is not about writing less code, this is about writing better code and more verifiable code. Later on we may change our code and not realize we accidentally got rid of this one line. Our unit test will show us quickly that we have whereas we'd otherwise build the assembly, register the plugin, go into CRM and update an account before we got an error and a little upfront code in a unit test will save you lots in the end. If I didn't believe this I would be spending all this time blogging about it.


Let's continue, shall we?


We've got a passing test, but first I want to refactor some things. As I mentioned before, refactoring is important in TDD. Not only for your production code but refactoring your test cases is immensely important in having easily maintainable test cases. You'll find yourself often wondering why you're writing test cases when you could just write the code and "be done" and having unmanageable test cases will only exacerbate that.


Refactoring is as much an art as anything. I do not pretend that I'm skillful at it but after some simple refactorings I get the following test cases (I have no need to refactor my plugin yet, except for maybe putting it in a proper file but I'll do that later):



[TestFixture]
public class PluginTestCases
{
private Mockery mock;
private IWebServiceWrapper webService;
private IPluginExecutionContext context;

[SetUp] // executes at the beginning of each test case. Executed only once per TextFixture
public void Setup()
{
mock = new Mockery();
webService = mock.NewMock();
context = mock.NewMock();
}

[Test]
public void New_DefaultDependencyIsCreatedCorrectly()
{
DemoPlugin plugin = new DemoPlugin();
Assert.IsInstanceOfType(typeof (MyWebService), plugin.webService);
}

[Test]
public void New_DependencyInjectionTakes()
{
DemoPlugin plugin = new DemoPlugin(webService);
Assert.AreEqual(webService, plugin.webService);
}

[Test]
public void Execute_EntityImageIsReadFromContext()
{
DemoPlugin plugin = GetDependencyInjectedPlugin();

DynamicEntity account = CreateSampleAccountDEWithPopulatedValues();
PropertyBag postEntityImages = CreatePropertyBagWithAccount(account);
SetupExpectationsThatPostEntityImagePropertyBagIsRead(postEntityImages);

plugin.Execute(context);
mock.VerifyAllExpectationsHaveBeenMet();

}

private DemoPlugin GetDependencyInjectedPlugin()
{
return new DemoPlugin(webService);
}

private void SetupExpectationsThatPostEntityImagePropertyBagIsRead(PropertyBag postEntityImages)
{
Expect.Once.On(context).GetProperty("PostEntityImages").Will(Return.Value(postEntityImages));
}

private PropertyBag CreatePropertyBagWithAccount(DynamicEntity account)
{
PropertyBag postEntityImages = new PropertyBag();
postEntityImages["postImage"] = account;
return postEntityImages;
}

private DynamicEntity CreateSampleAccountDEWithPopulatedValues()
{
// the account entity that fired the update
DynamicEntity account = new DynamicEntity("account");
account["accountid"] = new Key(Guid.NewGuid());
account["accountnumber"] = "someAccountNumber";
account["name"] = "accountName";
return account;
}
}



All I did here was rearrange some code to make it more readable. I can verify this by running all my tests again and seeing that they still all pass. A good refactoring tool like Resharper will greatly help in refactoring and make you much more likely to do it.

A few points here that can be refactored/improved upon:

  • The key value for the property bag "postImage" should probably be pulled into either a configurable option or a const value. Configurable is nice but it's probably something you just need in documentation for the implementer of the plugin so that when they register it in the CRM system they know what images to setup the plugin to receive.
  • Regarding deployment (this isn't TDD specific, just a point I like to follow), when developing your plugin you will likely be testing against CRM directly (remember, TDD is a development tool, not a testing tool). You'll have to use one of the registration tools. It's a good idea to export your registration settings into an XML file and store that in your project for reference. This can also be included easily then in a deployment package (msi file or otherwise) to ease deployment on the implementer.

That's it for this part. Stay tuned for part 3 where I show the testing of the call to the webservice.


=-}

Monday, July 14, 2008

TDDing a CRM 4.0 Plugin - Part 1

I was recently introduced to the wonderful world of TDD and I'll never turn back. I've spent less time with headaches, bugs and waiting for an IISReset to complete.

However, when I got back into the world of CRM I was surprised to find little about how to TDD a CRM Plugin.

If you are unfamiliar with TDD, you can find a useful primer over at wikipedia. I'll cover the basics of TDD here in the first post and then cover CRM specifics in later posts.


For this example I will be using NUnit as the testing framework and NMock for mocking interfaces. You may also see some traces of Resharper. While by no means required to do proper TDD development you'll find your productivity greatly increased by using this tool (and no, they aren't paying me to say that. But if they wanted to I'd gladly accept their money).

We'll be writing a plugin that is probably over simplistic. Upon account creation or updation, it will call a webservice. Integration with other systems is probably the thing I am asked most to write so this should cover a pretty common scenario.

Let's get started.

We'll begin with the basic NUnit test fixture and create a simple test case. Our dependency for CRM is handled by the PluginContext object that is passed in to us during runtime. But, we've also got a dependency on the webservice we'll be calling to. We need to make sure that our dependency is created correctly. This makes for a tightly coupled plugin but from my experience, that's OK. I doubt I'd take this approach in a fullblown application as a loosely coupled system usually makes for an easier system to maintain. However, for the sake of this example, I'm comfortable with it. The test case and test fixture end up like this:



Notice how I've created just enough code for this to compile and for the test to run. Resharper helps out immensly here as it recognizes what's missing and can create basic classes and interfaces with types inferred from method parameters and such. I've got the interface for my webservice, a simple instance of it, and my plugin class itself.

I run the unit test now and...

And good, I get my red/failed test. By reviewing the failure I can see that my unit test is correctly failing because what I expected (an instance of my MyWebService class) was null. Perfect. Now we'll write the implementation code to make the unit test pass: And then re-run the unit test: And voila! We have our test passing so we know our dependencies will be setup correctly.

However, when I unit test I want these to be "unit" tests and not integration tests. Therefore, I need to be able to inject my dependency. This allows me to ensure that calls to my webservice are happening correctly without actually standing up a webservice to receive those calls.

So, I'll write my unit test. Again, I've written just enough code to let it all compile but nothing to actually make the test pass. I run it and can see it correctly failing:

I'll go ahead and write the code to make it pass: And the passing test case: Now, a very important part of TDD is refactoring. Refactoring is important as it helps create reusable and maintainable code.

Right now this code has a fatal flaw... if I later write code in one constructor, I have to write it in both constrcutors. Writing code twice is the first indication that you need to refactor. So, let's rewrite the constructors a bit to see if we can remove this duplication. By making my default constructor call my dependency injected constructor I can ensure that any code I need to add to my constructor only needs to be added to the dependency injected constructor:


Now I can rerun my two tests and see they still pass so I know my code still has the same end effect:

At this point I should also refactor my unit tests, but there is little if anything I could do here so I'll skip it. Do not refactor your code and tests at the same time. I also need to refactor my classes and interfaces into correct file structures but I won't cover that here (again, Resharper or another refactoring tool can help immensly with this).

There is a lot of discipline involved with TDD. It may seem like a lot of work upfront but from my experience with it that work upfront will save you more than enough work on the backend and makes it worth it. Making sure your tests fail first, being thorough in your test cases and effective refactoring will make for successful TDD development. TDD is a developing ideology and not a testing one. The name is misleading. By requiring the coder to write the unit tests first it makes him/her throughly decide on the input an output of a procedure without getting involved in the implementation details. By no means should unit testing be done in lieu of integration, system and user testing. If anyone thinks that unit testing is enough, remind them of the Mars Polar Lander (which likely failed because while the individual systems were unit tested the whole was not properly system tested).

Now, this was only a basic introduction into the world of TDD. I suggest you read some more on your own. Stay tuned for more posts that dive into specifics regarding TDD with the CRM API.

=-}