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.



Anonymous said...

Hi Matthew,

May i know where can i reference the IWebServiceWrapper ?


Hadi Teo.

Anonymous said...

Hi Matthew,

is it possible to provide the full source code of your code sample ?


Hadi Teo.

Alan Bigham said...

Hi there - the image links to code samples are broken. I know they were of poor quality before but poor is better than none ;-)

Alan Bigham