Wednesday 6 February 2013

Mocking Frameworks for .NET



The four major competitors in the free/open source .NET mocking framework arena are microsoft .netNMock/NMock2, NMock3, Rhino Mocks and MOQ. NMock and NMock2 were actually built by different teams, but they kept the same design philosophy and are backwards compatible so they can be used almost interchangeably. If you're familiar with NMock/NMock2, you'll see that NMock3 is from the same gene pool, but it starts fresh and has somewhat different syntax from its siblings. All of the major mocking libraries are mature products with a decent-sized user base, so it's not tough to find good examples and help for all of them but NMock3.

Like many other .NET developers, I started using NMock, and then "upgraded' to nMock2, but both use "magic strings" which can't take advantage of Intellisense, and make tests brittle because they're not easily amenable to refactoring without the use of additional tools like ReSharper to replace name-similar text in strings. On the plus side, NMock doesn't rely on the explicit record/replay statements as Rhino Mocks does. The NMock2 codebase hasn't been updated since late in 2009, and should not be expected to see continued development.

On the plus side, Rhino Mocks has a syntax that supports code refactoring and compile-time checking. Unfortunately, I don't find the syntax to be particularly intuitive, and the variety of ways to construct and condition mocks can lead to some confusion when different people are writing tests. There is also the annoying need to write explicit record and replay statements. Another problem with Rhino Mocks is that the project appears to be running out of steam. Aside from a few bug-fix patches, not much has happened since early in 2009.

MOQ is one of the new kids, with an annoying name that needs to be spelled out so that people know what you're talking about. The latest release became available in April of 2011, and MOQ has active developers and community. It's different from the first two in several ways. When using NMock and Rhino you create mock objects of a specific type. When using MOQ you create mocks that contain an objects of a specific type, and that wrapper around the mock objects lends itself to simpler syntax in your tests. MOQ requires .Net 3.5 or greater due to its use of lambdas, so if you haven't learned to use lambda expressions, now is the time. I haven't used it extensively, but so far I like everything about MOQ... except for the name.

NMock3 is the other new kid, and the one who seems less well known. Maybe it isn't on the radar for most folks because NMock/NMock2 seem behind the times, and that may have kept expectations low. Versions of NMock3 are available for .NET 3.5 and 4.0, and the latest RTM became available in January of 2011 with a beta release in July of 2011. Like MOQ, you create mocks that contain objects of a specific type, and that wrapper around the mock objects leverages lambda expressions, so NMock/NMock2 users are in for some culture shock. Since the NMock3 examples are a little thin in the wild I've include sample code below. Note that in conditioning your mocks, the parameters in the lambda expressions are just placeholders, while the "real" parameters are specified in the "With" method.
using NUnit.Framework;
using NMock; /* NMock3 */

namespace Foo.Test
{
    [TestFixture]

    public class FundsTransferPresenterTest
    {
        private MockFactory _mocks;
        private Mock<IFundsTransferView> _viewMock;
        private Mock<IAccountService> _serviceMock;
        private FundsTransferPresenter _presenter;

        [SetUp]
        public void SetUp()
        {
            _mocks = new MockFactory();
            _viewMock = _mocks.CreateMock<IFundsTransferView>();
            _serviceMock = _mocks.CreateMock<IAccountService>();
            _presenter = new FundsTransferPresenter(_viewMock.MockObject, _serviceMock.MockObject);
        }

        [Test]
        public void CanQueryViewUseAccountServiceToFundsTransfer()
        {
            _viewMock.Expects.One.Method(v => v.GetSourceAccount()).WillReturn("1234");
            _viewMock.Expects.One.GetProperty(v => v.TargetAccount).WillReturn("9876");
            _viewMock.Expects.One.GetProperty(v => v.TransferAmount).WillReturn(200.00m);
            _serviceMock.Expects.Exactly(1).Method(s => s.TransferFunds(null, null, 0m)).With("1234", "9876", 200.00m);
            _presenter.Transfer_Clicked();
            _mocks.VerifyAllExpectationsHaveBeenMet();
        }
    }
}

/*******************************   Fragment of System Under Test (SUT)   *******************************/

    public interface IFundsTransferView
    {
        string GetSourceAccount();
        string TargetAccount { get; }
        decimal TransferAmount { get; }
    }

    public interface IAccountService
    {
        void TransferFunds(string source, string target, decimal amount);
    }

    public class FundsTransferPresenter
    {
        private IFundsTransferView _view;
        private IAccountService _service;

        public FundsTransferPresenter(IFundsTransferView view, IAccountService service)
        {
            _service = service;
            _view = view;
        }

        public void Transfer_Clicked()
        {
            _service.TransferFunds(_view.GetSourceAccount(), _view.TargetAccount, _view.TransferAmount);
        }
    }

Source: Robert Zormeir

No comments:

Post a Comment

Note: only a member of this blog may post a comment.