I am trying to write Unit Tests for an existing Sitecore Project on MVC5 that uses GlassMapper ORM and TDS. I have mocked the Sitecore Content Tree, Sitecore Context, Item Properties, Field Collections and Base Item using this extremely Beneficial Blog Post by Jeff Sulit.
https://oddeven.ch/blog/mocking-sitecore-using-ms-fakes/
Now the setup established in the above post (shared as link above.... please go through it before answering). I have kept the above setup inside a separate class file UnitTestAssist.cs in my Business Layer.
Now i have written the following basic unit test in which i create a fake item from my unit test Project and then access it through an object of UnitTestAssist class object by Sitecore.Context,Database.GetItem call which returns null.
namespace MySiteCoreUnitTest
{
[TestClass]
public class DataFetch
{
IDisposable _context;
readonly MySiteCoreUnitTest.UnitTestAssist _scFaker = new MySiteCoreUnitTest.UnitTestAssist();
[TestInitialize]
public void Initialize()
{
_context = ShimsContext.Create();
_scFaker.Initialize();
}
[TestCleanup]
public void Cleanup()
{
_context.Dispose();
}
[TestMethod]
public void TestingDataFetch()
{
List<ShimField> lstfields = new List<ShimField>();
//Arrange
ShimField dummyField = new ShimField()
{
IDGet = () => ID.NewID,
NameGet = () => "Ava_Title",
ValueGet = () => "This is Crazy"
};
lstfields.Add(dummyField);
var expectedItem = _scFaker.CreateFakeItem(_scFaker.Home, "sample item", (itemName, inputField) => { inputField = lstfields; });
// Act:
var actualItem = Context.Database.GetItem(expectedItem.Instance.Paths.FullPath);
var actualItemFields = actualItem.Fields; // On Debugging i get the field i added forcefully inside **UnitTestAssist**.
// Assert:
Assert.AreEqual(expectedItem, actualItem);
}
}
}
Here is the code that i have used for creating Mock Items.
namespace MySiteCoreUnitTest
{
public class UnitTestAssist
{
private readonly Language ContextLanguage = Language.Parse("en");
ShimItem CreateFakeItem(ShimItem parentItem, string name, Action<ShimItem, ShimTemplateItem, List<ShimField>> onItemCreating)
{
var id = ID.NewID;
var item = new ShimItem()
{
// Assigning ID.NewID directly will return a new ID every time item.ID is called
IDGet = () => id,
KeyGet = () => name.ToLower(),
NameGet = () => name,
HasChildrenGet = () => false,
ParentGet = () => parentItem,
PathsGet = () =>
{
var path = (parentItem != null ? parentItem.Instance.Paths.Path : "") + "/" + name;
return new ShimItemPath()
{
PathGet = () => path,
FullPathGet = () => path,
};
},
LanguageGet = () => ContextLanguage,
VersionsGet = () => new ShimItemVersions() { CountGet = () => { return 1; } }
};
// Bind item to parent item
if (parentItem != null)
{
var children = parentItem.Instance.HasChildren ? parentItem.Instance.Children.ToList() : new List<Item>();
children.Add(item);
parentItem.HasChildrenGet = () => true;
parentItem.ChildrenGet = () => new ChildList(parentItem.Instance, children);
parentItem.GetChildren = () => parentItem.Instance.Children;
}
// Start faking template and field collection
var templateItem = new ShimTemplateItem();
var field = new ShimField
{
};
//var fields = new List<ShimField>();
List<ShimField> fields = new List<ShimField>();
//Arrange
ShimField dummyField = new ShimField()
{
IDGet = () => ID.NewID,
NameGet = () => "Ava_Title",
ValueGet = () => "This is Crazy"
};
fields.Add(dummyField);
// Call action to allow extension of item, template, and field collection faking
onItemCreating(item, templateItem, fields);
item.TemplateGet = () => templateItem;
item.FieldsGet = () => CreateFakeFieldCollection(item, fields);
return item;
}
// Create a dictionary to hold the field collection per item ID
private Dictionary<ID, List<ShimField>> itemFields = new Dictionary<ID, List<ShimField>>();
ShimFieldCollection CreateFakeFieldCollection(ShimItem item, List<ShimField> fields)
{
foreach (var field in fields)
field.ItemGet = () => item;
ShimField _newField = new ShimField();
var fieldCollection = new ShimFieldCollection()
{
ItemGetString = (fieldName) =>
{
_newField = fields.SingleOrDefault(n => n.Instance.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase));
return fields.SingleOrDefault(n => n.Instance.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase));
}
};
Field testField = _newField;
if (!itemFields.ContainsKey(item.Instance.ID))
itemFields.Add(item.Instance.ID, fields);
else
itemFields[item.Instance.ID] = fields;
fieldCollection.Bind(itemFields[item.Instance.ID]);
return fieldCollection;
}
void FakeSitecoreContext()
{
ShimContext.LanguageGet = () => ContextLanguage;
ShimContext.SiteGet = () => new ShimSiteContext()
{
ContentLanguageGet = () => ContextLanguage
};
Func<Func<Item, bool>, Item> getItem = (predicate) =>
{
Item result;
return TryGetItem(this.Sitecore.Instance.Children, predicate, out result) ? result : null;
};
ShimContext.DatabaseGet = () => new ShimDatabase()
{
GetItemString = (path) => getItem(n => n.Paths.Path.Equals(path, StringComparison.OrdinalIgnoreCase)),
GetItemStringLanguage = (path, lang) => getItem(n => n.Paths.Path.Equals(path) && (n.Language.Equals(lang) || n.Languages != null && n.Languages.Any(l => l.Name.Equals(lang.Name)))),
GetItemID = (id) => getItem(n => n.ID.Equals(id)),
GetItemIDLanguage = (id, lang) => getItem(n => n.ID.Equals(id) && (n.Language.Equals(lang) || n.Languages != null && n.Languages.Any(l => l.Name.Equals(lang.Name)))),
};
}
bool TryGetItem(ChildList children, Func<Item, bool> predicate, out Item result)
{
result = null;
if (children == null || !children.Any()) return false;
result = children.FirstOrDefault(predicate);
if (result != null) return true;
var query = children.Where(n => n.HasChildren);
if (!query.Any()) return false;
foreach (var child in query.ToArray())
{
if (TryGetItem(child.Children, predicate, out result))
return true;
}
return false;
}
void FakeBaseItem()
{
ShimBaseItem.AllInstances.ItemGetString = (baseItem, fieldName) =>
{
Item result;
TryGetItem(Sitecore.Instance.Children, (n) => object.Equals(baseItem, n), out result);
if (result != null)
{
var fields = itemFields[result.ID];
var field = fields.FirstOrDefault(n => n.Instance.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase));
if (field != null) return field.Instance.Value;
}
return string.Empty;
};
}
public ShimItem Sitecore, Content, Site, Home;
public void Initialize(Action<UnitTestAssist> onInitializing = null)
{
Sitecore = CreateFakeItem(null, "sitecore", (sitecore, someFieldSitecore) =>
{
Content = CreateFakeItem(sitecore, "content", (content, someFieldContent) =>
{
Site = CreateFakeItem(content, "site", (site, someFieldSite) =>
{
Home = CreateFakeItem(site, "home", (home, someFieldHome) =>
{
// Add more items if you must to mimic your Sitecore tree
});
});
});
});
if (onInitializing != null)
onInitializing(this);
FakeBaseItem();
FakeSitecoreContext();
}
public ShimItem CreateFakeItem(ShimItem parentItem, string name, Action<ShimItem,List<ShimField>> onItemCreating)
{
return CreateFakeItem(parentItem, name, (i, t, f) =>
{
if (onItemCreating != null)
onItemCreating(i,f);
});
}
}
}
I have modified Jeff's code to be able to accept List<ShimField>
as argument from the Unit Test but it still sends item field as null. Jeff can you help?
Hard to tell without seeing your actual code for the 'UnitTestHelperClass' class. Perhaps it'd be useful if you could share that as well.
UPDATE: I've refactored your code to resolve your issue:
var expectedItem = _scFaker.CreateFakeItem(_scFaker.Home, "sample item", (itemName, inputField) => { inputField.AddRange(lstfields); });