Random Musings

June 11, 2009

TDD with Dynamics CRM 4.0 Plugin

Filed under: Dynamics CRM — haditeo @ 4:24 pm
Tags: , ,

Download source code here

I would like to introduce how to enforce TDD for developing plugin in Dynamic CRM. Why enforcing TDD ? I find it very time consuming to manually enter the data from the Dynamics CRM UI, just to trigger the plugin to run and generate the IPluginExecutionContext.

Usually i hook the debugger on the plugin codes right inside the IPluginExecutionContext method and debug line by line, then if some bugs are detected, then i need to
1) Recompile
2) Re-register again through plugin registration tool
3) IISReset
4) Manually enter the data again
5) Debug the codes
These cycles are repeated numerously, which are time consuming

The key is how do we “mock” IPluginExecutionContext ?

Take a look at the definition of the Execute method which accepts IPluginExecutionContext type of object :

public void Execute(IPluginExecutionContext context)
{
    ContextStage = context.Stage;

    List = QueryActiveContact(context.CreateCrmService(true));
}

In this method, i would like to
1) Mock the context.Stage property to return some fake value
2) Run a query against Dynamics CRM SDK by mocking the CrmService returned by context.CreateCRMService() method

Here i am using RhinoMock as my mocking framework:

[Test]
public void TestInstantiate()
{
    TestIPluginExecutionContext test = new TestIPluginExecutionContext();           

    MockRepository mocks = new MockRepository();
    IPluginExecutionContext context = mocks.StrictMock<IPluginExecutionContext>();

    With.Mocks(mocks).Expecting(delegate
    {
        Expect.Call(context.Stage).Return(11122);
        Expect.Call(context.CreateCrmService(true)).Return(new MockCrmService());
    })
    .Verify(delegate
    {
        test.Execute(context);
    });

    Assert.Greater(test.ContextStage, 0, "context stage is 0");
    Assert.IsNotEmpty(test.List, "list is empty");
}

The key here is i am using Expect.Call() method, to tell mocking framework to return ‘11122’ integer value when context.Stage is invoked . If the context.CreateCrmService() method is invoked, please return a concrete MockCrmService object. I have created a fake MockCrmService object, to be injected when the context.CreateCrmService() method is called.

I have noticed that the Dynamics CRM plugin has confirmed to TDD, by making the parameter of the method of Execute accepting an Interface type of IPluginExecutionContext. This is very useful, since during the implementation we can mock / fake the CRM Service by impersonating any CRM user. The advantage of this is to test whether any exception is occured when we impersonate a CRM user to access a record.

Here is the concrete MockCrmService object

public class MockCrmService : ICrmService
{
    public CrmService CreateCrmService()
    {
        NetworkCredential nc = new NetworkCredential();
        nc.Domain = Settings.Default.Domain;
        nc.UserName = Settings.Default.UserName;
        nc.Password = Settings.Default.Password;

        CrmAuthenticationToken token = new CrmAuthenticationToken();
        token.AuthenticationType = Convert.ToInt32(Settings.Default.AuthenticationType); ;
        token.OrganizationName = Settings.Default.OrgName;

        CrmService crmService = new CrmService();
        crmService.CrmAuthenticationTokenValue = token;
        crmService.Credentials = nc;
        crmService.Url = Settings.Default.CrmUrl;

        return crmService;
    }

    #region ICrmService Members

    public Guid Create(BusinessEntity entity)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public void Delete(string entityName, Guid id)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public object Execute(object request)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public string Fetch(string fetchXml)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public BusinessEntity Retrieve(string entityName, Guid id, Microsoft.Crm.Sdk.Query.ColumnSetBase columnSet)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public BusinessEntityCollection RetrieveMultiple(Microsoft.Crm.Sdk.Query.QueryBase query)
    {
        return CreateCrmService().RetrieveMultiple(query);
    }

    public void Update(BusinessEntity entity)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion
}

Since we are interested in using RetrieveMultiple method, i inserted codes below to instantiate a new CrmService object

    public BusinessEntityCollection RetrieveMultiple(Microsoft.Crm.Sdk.Query.QueryBase query)
    {
        return CreateCrmService().RetrieveMultiple(query);
    }

For the full source code, please refer to the download link in the beginning of the post.
What are the advantages of using TDD in developing plugin ?
1) We can “mock” the IPluginExecutionContext to supply correct data to test our internal business logic.
2) We can “mock” the IPluginExecutionContext to supply incorrect data to test whether the exception raised by our business logic are correct
3) We can combine this with NUnit to run our test cases automatically, without needing manual intervention. Since we build small codes and test small codes, it’s quite easy to detect bugs since we have NUnit to alert us in the event of bugs raising.
4) We don’t need to publish the plugin just to test the plugin, since publishing is time consuming and involving IISReset to refresh the assembly.
5) We can utilize NUnit to put assertion, to detect whether the object values or states are correct, rather than manually put the debugger when the plugin is running.

Advertisements

1 Comment »

  1. Nice to find some beautifull code about CRM Plugin + TDD !
    Thanks for this post !

    Comment by Gustavo — April 23, 2010 @ 2:47 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: