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.


=-}

8 comments:

Anonymous said...

I really enjoyed reading your post – although I’m having problems reading the code :)

I have a problem - At work I need to write a component that retrieve a list of leads from our CRM. And I can’t find how to mock an entity object using NMock.

Matthew Bonig said...

Beni,

Sorry about the code. I've been really busy and haven't been able to update the posts with more readable code. I hope to get to it this weekend.

As for mocking an entity object... NMock is only able to mock interfaces, and not classes. Since the CRM API doesn't provide interfaces to their objects, you can't user NMock to mock out any entities.

In this case, I would suggest just creating instances of the classes. Since CRM entities are just DTO's, you really don't gain anything by mocking them anyway. Mocking is used to simulate behavior. Since DTO's have no true behavior, it's unneccessary.

I would suggest something like this:

account testAccount = GetFullyPopulatedAccount();

and then the GetFullyPopulatedAccount method will return a new instance of an account object with all the various fields pre-populated. I also suggest you make sure you test your code against cases where "account" has no fields populated. Although it shouldn't happen through the normal flow of work in CRM, it allows you to easily make sure your code isn't forgetting to check for null values.

If you're feeling real adventurous, there are other mocking solutions out there that can mock classes since NMock can't. However, you won't gain much by using them unless your tests are checking to see if specific fields on the entity are getting updated (something that's tough to do without mocking).

Hope this helps. Sorry again about the code not being readable =-/

Anonymous said...

Thanks for the quick replay.

I guess I could create a GetFullyPopulatedAccount() method like you suggested but what I really need is to use the “real” retrieve function call to get the CRM leads.

Is there a way to use the retrieve function to get a predefined lead (if possible without calling the CRM server)?

Matthew Bonig said...

Beni,

You're now encountering one of the trickier parts of TDDing with CRM. The issue here is that you need to simulate the call to retieve a record from CRM without making the call itself (otherwise, if you made the call, it wouldn't be a unit test).

The TDD series I'm doing was going to conclude with a post about how to do this. However, long story short, you have two options:

1. Mock the ICrmService interface from the plugin context (which you're mocking anyway) and have an NMock 'Expect' call against the ICrmService to simulate the call to the service. The object you tell the Expect to return is the Lead object you're wanting to work with. However, you have to create that Lead object by hand and you may not create the same object CRM will actually give you back during runtime.
2. All the same as above, except instead of having to create a CRM object by hand, you have one that a previous CRM service call gave you. I can't go into the detail here in just a comment but I will try to get a post up about how to do this properly.

In the meantime, until I can details out on option #2, I would just create one by hand. If you've been working with the CRM Web Service and the object model much at all you can usually do a pretty good job in creating your own by hand that's pretty close to the object that the webservice will actually return during runtime.

Plus, if you are unit testing your code correctly (usually through extensiveness) then any discrepencies between the CRM webservice call results during runtime and the object you create by hand should be handled through proper logging and exception handling.

I'm sorry I can't go into more detail here, but I'd go down the path of just having the Expect against the ICrmService mock object return an object you created manually, as it's the easiest to handle.

Thanks

Ohad said...

Hi

Interesting discussion.
It seems that you are fighting against the limitations of NMock.
In your previous answer you mentioned other mocking frameworks. One of them is Typemock Isolator.
The Isolator really shines when you need to mock third party types since it can mock concrete types and not just interfaces and abstract classes.
So in the case above to create an entity you can just create an entity mock object than mock the calls that update the database.

Something like this:
MockObject entityMock = MockManager.CreateMockObject(typeof(DynamicEntity));
entityMock.ExpectCall("Update");

DynamicEntity dynamicEntity = entityMock.Object as dynamicEntity;
// More code that uses dynamicEntity

Ohad
Typemock

Matthew Bonig said...

@Ohad,

You are absolutely correct. There are some limitations of NMock that have to be creatively worked around at times.

However, I wouldn't exactly shy away from doing this type of thing as you'll sometimes have to be more creative in your test cases then you are in your production code and it's good to have the practice. There have been more then a few times where I've had to dig into the guts of the NMock framework to get something accomplished.

I went with NMock here as it's "free". TypeMock, although they provide a free edition, is only free for internal or personal use. If you are a contractor like I am, this prohibits you from using it for a client unless they decide to shell out the money for it. If you have the opportunity I fully suggest doing it. The inability to mock concrete objects is a sore spot for me with NMock but it has never stopped me from using it so far. (I've either been testing code that is entirely my own, or the object I wish I could mock was just a DTO in which case I wasn't worried too much about it)

There are plenty of other mocking frameworks out there. I'd start with some of the more popular ones listed at Wikipedia's Mock Object page. I've been particularly interested in trying RhinoMock.

=-}

Matthew Bonig said...

@Ohad,

You are absolutely correct. There are some limitations of NMock that have to be creatively worked around at times.

However, I wouldn't exactly shy away from doing this type of thing as you'll sometimes have to be more creative in your test cases then you are in your production code and it's good to have the practice. There have been more then a few times where I've had to dig into the guts of the NMock framework to get something accomplished.

I went with NMock here as it's "free". TypeMock, although they provide a free edition, is only free for internal or personal use. If you are a contractor like I am, this prohibits you from using it for a client unless they decide to shell out the money for it. If you have the opportunity I fully suggest doing it. The inability to mock concrete objects is a sore spot for me with NMock but it has never stopped me from using it so far. (I've either been testing code that is entirely my own, or the object I wish I could mock was just a DTO in which case I wasn't worried too much about it)

There are plenty of other mocking frameworks out there. I'd start with some of the more popular ones listed at Wikipedia's Mock Object page. I've been particularly interested in trying RhinoMock.

=-}

Ohad said...

Hi Metthew
I checked it out there is no limitation that prohibits you from using Typemock at a client machine.
The client can use the free addition with no limitations.

You can get special contractor license for Typemock.
If you are interested, you can contact our marketing manager: avik at typemock dot com

Ohad
Typemock