Jun 08 2012

CRM2011 Automated Testing – Integration Testing

Published by at 3:34 pm under .NET,General,Microsoft CRM

The integration testing is concerned with testing customisations within the context of CRM to ensure that the various components work together correctly. In my current work this is mostly related to custom plugins in CRM and custom integration with SharePoint. The key point is that with this type of testing I am not concerned with the user interface, rather the underlying processes.

In many ways the integration tests are very similar to the unit tests, they are code based test created using the Gallio/MbUnit framework. However, instead of testing code in isolation and mocking dependencies, the integration tests test whole systems. This is achieved by making create, update, delete, etc. calls to the web service to simulate user actions.

Test setup and tear down code can become length as you need to be able to setup scenarios to be able to test, including creating test records and enabling/disabling plugin to allow actions to be performed that may otherwise be blocked.

It is also during integration testing that I introduce testing as specific users to test security related constraints. This is easily achieved in CRM as connections to the Organization Service support impersonation.

Integration tests are also good where a user has identified an issue with a plugin in a scenario you hadn’t thought of before. I tend to use this as a trigger to create new integration test to match that case, and then one or two more boundary cases around it.

As these tests require connection and interaction with CRM, they can be slow to run. As a result, I tend to run these in TeamCity, where they can be left to run on their own, or I will run specific tests in order to help track down an issue. Whilst setting them up may seem to take a long time, relative to “productive development” time, they soon become invaluable for regression testing as new functionality is developed or existing code is refactored to improve performance, etc.

Example:

namespace Tests.Plugin.AddressTests
{
    public class AddressTests
    {
        #region Setup/Teardown

        [SetUp]
        public void BeforeEachTest()
        {
            _testEntity = createEntityForTests();
        }

        [TearDown]
        public void AfterEachTest()
        {
            if (_testEntity != null)
            {
                _testEntity.DeleteAndIgnoreException();
            }
        }

        #endregion

        #region Instance variables

        protected CrmEntity _testEntity;
        protected static readonly string ExpectedExceptionMessageContains = "This record already has an active";

        #endregion

        [Test]
        public void TestMultipleMailingAddressesCanBeUnchecked()
        {
            setAddressPluginState(PluginStepState.Disabled);
            var address1 = CustomerAddressBuilder.NewMailingAddress(_testEntity.ToEntityReference(), "Mailing 1");
            var address2 = CustomerAddressBuilder.NewMailingAddress(_testEntity.ToEntityReference(), "Mailing 2");
            var address3 = CustomerAddressBuilder.NewMailingAddress(_testEntity.ToEntityReference(), "Mailing 3");
            setAddressPluginState(PluginStepState.Enabled);

            address1.C5_Mailing = false;
            address1.Save();

            setAddressPluginState(PluginStepState.Disabled);
            address1.Delete();
            address2.Delete();
            address3.Delete();
            setAddressPluginState(PluginStepState.Enabled);
        }

        #region Protected Methods

        protected enum PluginStepState
        {
            Enabled = 0,
            Disabled = 1
        }

        protected void setAddressPluginState(PluginStepState state)
        {
            using (var context = ServicesFactory.CrmContext())
            {
                var steps = from step in context.CreateQuery<SdkMessageProcessingStep>()
                            where step.Name.Contains("Address")
                            select new SdkMessageProcessingStep { SdkMessageProcessingStepId = step.SdkMessageProcessingStepId, Stage = step.Stage };

                foreach (var step in steps)
                {
                    EntityReference entityref = step.ToEntityReference();

                    SetStateRequest req = new SetStateRequest();
                    req.EntityMoniker = entityref;
                    req.State = new OptionSetValue((int)state);
                    req.Status = new OptionSetValue(-1);

                    SetStateResponse resp = (SetStateResponse)context.Execute(req);
                }
            }
        }

        #endregion
    }
}

In this example you can see that the test is disabling a plugin, setting up some test address records, re-enabling the plugin and then executing the test. After the test is complete it cleans up the test records.

Related Posts

No responses yet