Unit Testing: Which Framework?

Unit Testing

So we were having a discussion in our team, over which testing frameworks people had used and which they thought were best. I went on to do a bit of research and there were a variety of articles out there leaning heavily in one way or the other.

So I decided to do a quick test myself, to see if I found one better that the other.

To do this, I looked at MSTest vs xUnit test frameworks, and Moq / nSubstitute for mocking dependencies. I had previously written some unit tests for an ImageTaxonomy class in this article using nSubstitute and xUnit. So to make my comprison, I set out to write the same set of tests using MSTest and Moq (which you can see at the bottom of this post).

A few of the differences can be seen below:

MSTest vs xUnit

Both are very similar in the small amount of examples I tried.

Test Method Attribute

MSTest:

[TestMethod]

xUnit:

[Fact]

Assertions

MSTest:

var ex = Assert.ThrowsException<ArgumentNullException>(sutAction);
Assert.AreEqual("mediaManager", ex.ParamName);
Assert.AreEqual(String.Empty, results);

xUnit:

var ex = Xunit.Assert.Throws<ArgumentNullException>(sutAction);
Assert.Equal("mediaManager", ex.ParamName);

Assert.Empty(results);

MSTest doesnt have an Empty Assert option.

Moq/nSubstitute

Creating the Mock

Moq:

var field = new Mock<Field>(ID.NewID, item);

nSubstitute:

var field = Substitute.For<Field>(ID.NewID, item);

When you want to use object in Moq you need to do:

field.Object

Properties

Moq:

field.SetupGet(x => x.Value).Returns(fieldValue);

nSubstitute:

field.Value = fieldValue;

Methods

Moq:

mediaManager.Setup(_ => _.GetMediaUrl(It.IsAny<MediaItem>())).Returns(imageUrl);

nSubstitute:

mediaManager.GetMediaUrl(Arg.Any<MediaItem>()).Returns(imageUrl);

The reality is they are both very similar to use, I didnt come across one thing one could do but the other couldnt. Neither is particularly difficult to use, especially if there exists some examples already in a project you have been asked to work on. I think people should just use what they want, then if you pick up a project to work on, just continue using the same as which has been adopted already.

Conclusion

My preference was nSubstitute, as it looks cleaner.


For comparison, here is my ImageTaxonomy class being tested using MSTest and Moq:

[TestClass]
public class ImageTaxonomyTests
{
    private string mediaItemIDString = "12345678-1234-1234-1234-123456789012";
    private ID mediaItemID = new ID("12345678-1234-1234-1234-123456789012");

    [TestMethod]
    public void Ctor_MediaManagerIsNull_Throws()
    {
        // arrange
        Action sutAction = () => new ImageTaxonomy(null);

        // act, assert
        var ex = Assert.ThrowsException<ArgumentNullException>(sutAction);
        Assert.AreEqual("mediaManager", ex.ParamName);
    }

    [TestMethod]
    public void GetImageUrl_ItemIsNull_ReturnsEmpty()
    {
        // arrange
        var mediaManager = new Mock<BaseMediaManager>();
        var imageUrl = "~/Test-Image";
        mediaManager.Setup(_ => _.GetMediaUrl(It.IsAny<MediaItem>())).Returns(imageUrl);

        var sut = new ImageTaxonomy(mediaManager.Object);

        // act
        var results = sut.GetImageUrl(null, "Some Field");

        // assert
        Assert.AreEqual(String.Empty, results);
    }

    [TestMethod]
    public void GetImageUrl_FieldNameIsNull_ReturnsEmpty()
    {
        // arrange
        var database = new Mock<Database>();
        var mediaManager = new Mock<BaseMediaManager>();

        var item = CreateItem(database.Object);

        var sut = new ImageTaxonomy(mediaManager.Object);

        // act
        var results = sut.GetImageUrl(item.Object, null);

        // assert
        Assert.AreEqual(String.Empty, results);
    }

    [TestMethod]
    public void GetImageUrl_UnknownFieldName_ReturnsEmpty()
    {
        // arrange
        var database = new Mock<Database>();
        var mediaManager = new Mock<BaseMediaManager>();

        var item = CreateItem(database.Object);

        var sut = new ImageTaxonomy(mediaManager.Object);

        // act
        var results = sut.GetImageUrl(item.Object, "Some Unknown Field");

        // assert
        Assert.AreEqual(String.Empty, results);
    }

    [TestMethod]
    public void GetImageUrl_KnownFieldName_ReturnsValue()
    {
        // arrange
        var database = new Mock<Database>();

        var item = CreateItem(database.Object);
        SetItemField(item, "Some Known Field", $"<image mediaid='{mediaItemIDString}' />");

        var mediaItem = CreateMediaItem(database.Object);
        database.Setup(_ => _.GetItem(mediaItemID, It.IsAny<Language>(), Sitecore.Data.Version.Latest)).Returns(mediaItem);

        var mediaManager = new Mock<BaseMediaManager>();
        mediaManager.Setup(_ => _.GetMediaUrl(It.Is<MediaItem>(mi => mi.ID == mediaItem.ID))).Returns("/a/path/to/an/image.jpg");

        var sut = new ImageTaxonomy(mediaManager.Object);

        // act
        var results = sut.GetImageUrl(item.Object, "Some Known Field");

        // assert
        Assert.AreEqual("/a/path/to/an/image.jpg", results);
    }

    private Mock<Item> CreateItem(Database database = null)
    {
        var item = new Mock<Item>(ID.NewID, ItemData.Empty, database);

        var fields = new Mock<FieldCollection>(item.Object); 
        item.SetupGet(x => x.Fields).Returns(fields.Object);
        return item;
    }
    private Item CreateMediaItem(Database database = null)
    {
        var definition = new ItemDefinition(mediaItemID, "Mock Media Item", ID.Null, ID.Null);
        var data = new ItemData(definition, Language.Current, Sitecore.Data.Version.First, new FieldList());
        var mediaItem = new Item(mediaItemID, data, database);
        return mediaItem;
    }
    private void SetItemField(Mock<Item> item, string fieldName, string fieldValue)
    {
        var field = new Mock<Field>(ID.NewID, item);
        field.SetupGet(x => x.Value).Returns(fieldValue);
        field.SetupGet(x => x.Database).Returns(item.Object.Database);
        //item.Fields[fieldName].Returns(field);
        item.SetupGet(x => x.Fields[fieldName]).Returns(field.Object);

    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *