2
votes

I'm trying to find the correct approach to begin unit testing the piece of code below:

public class NameUtility
{
    private readonly string _folder = Settings.GetSetting("FolderId");
    public ISitecoreDatabase Database {get;set;} = new SitecoreDatabase();

    public virtual Path FindById(long Id)
    {
        var folder = Database.GetItem(new ID(_folder));
        var items = folder.GetChildren().Select(child => child.GlassCast<Path>());
        return paths.SingleOrDefault(path => path.PathId == Id);
    }
}

I'm quite new to unit testing so I chose the simplest utility I could find in order to test. This is the start of what my unit test looks like:

[TestFixture]
public class NameUtilityTests
{
    [Test]
    public void FindById_WithId_ReturnsPath()
    {
        var nameUtility = new NameUtility();
        nameUtility.Database = new FakeSitecoreDatabase();

        var path = nameUtility.FindById(1);

        Assert.AreEqual(1, path.PathId);
    }

    internal class FakeSitecoreDatabase : ISitecoreDatabase
    {
        public Item GetItem(ID id)
        {
            throw new System.NotImplementedException();
        }
    }
}

//Code below is from the Code Under Test assembly.
public interface ISitecoreDatabase
{
    Item GetItem(ID id);
}

public class SitecoreDatabase : ISitecoreDatabase
{
    private readonly Database _database;

    public SitecoreDatabase()
    {
        _database = Sitecore.Context.Database;
    }

    public SitecoreDatabase(Database database)
    {
        _database = database;
    }

    public Item GetItem(ID id)
    {
        return _database.GetItem(id);
    }
}

So I have a couple of questions and I'll explain my thought process first. FindById currently depends on Sitecore.Context.Database which wouldn't work for Unit Tests so we've used SitecoreFakeDb instead. I haven't implemented the GetItem method, but the idea would be that I setup an instance of SitecoreFakeDb with the item(s) and then the CUT should be able to fetch the item by id.

1) However to test this code I need to be able to substitute Sitecore.Context.Database with an instance of the FakeDb. For this, I've created ISitecoreDatabase and then it has methods like GetItem() and all its overloads that the default (actual) Sitecore db would implement as well as the FakeDb. Wouldn't this begin to get out of control as far as how many methods that interface would have?

2) When asserting against the final object, in this case I'm asserting that the object returned has a field with the same Id as what I expect. Is this a valid approach, or is the better approach to compare the entire actual object with an expected object?

1
You don't HAVE to substitute the database as you have already abstracted it behind an interface. you could just as easily mock the interface to preform as intended in the unit tests. As for the interface, it doesn't have to have all the overload. Only what is needed. You should only expose what is necessary anywayNkosi
Could you please provide a brief snippet/example of what you mean in your first statement?uioporqwerty
You should as the same question on stack exchange. sitecore.stackexchange.comChris Auer

1 Answers

0
votes

One option that would work for unit testing Sitecore will be to use a mock framework such as Moq.

By doing this, you can explicitly set up each unit test to only test the Item that is applicable to what you need to test. This keeps you from having to set up your database completely and allows you to test only what you should be testing in each unit test.

Here is an example of how you could write your test.

private Mock<IMyItemInDatabase> MyItemInDatabaseModel { get; set; };

[TestInitialize]
public void Setup()
{
    var mockSitecoreContext = new Mock<ISitecoreContext>();
    this.MyItemInDatabaseModel = new Mock<IMyItemInDatabase>();

    this.MyItemInDatabaseModel.SetupAllProperties();

    mockSitecoreContext.Setup(sc =>sc.GetItem<IMyItemInDatabase
        (It.IsAny<string>(), false, false)).
        Returns(this.MyItemInDatabaseModel.Object);
}

[TestMethod]
public void MyItemInDatabaseModel_Should_Not_Be_Null()
{
    //....perform unit test here
}