Mocking Sitecore with Microsoft Fakes part 1
This post have been in making for a while or since the Visual Studio 2012 Update 2 was announced. Especially since the update added Microsoft Fakes to VS 2012 Premium. With Fakes it is now possible to mock Static methods and otherwise unmockable class’ for example DataTime.Now or a Sitecore Item. So now finally it is possible to do unit testing with Sitecore or if you like TDD. I know that this has been possible for a while if you used Glass Mapper og had access to a Typemock license.
This post is the first in a small series, on how to mock Sitecore out using Microsoft Fakes, and to begin with, I will have a look and extracting data from a Sitecore Item.
The two ways to get data from item i will cover here is either accessing a field through the field collection using item.Field[name of your field] or by the indexing found on the BaseItem which Item derives from like this item[name of your field].
The two code snippets below is used to run my test against one uses Field[] the other Item[]
public class CodeSnippet { public static ID MY_TEXT_FIELD = new ID("{dc321650-5b1d-479a-ae81-c04d6585140d}"); public static ID MY_CHECKBOX_FIELD = new ID("{a007c875-5192-4068-864d-523ffaa0b4ca}"); public static string MISSING_TEXT = "MISSING TEXT"; public string RunWithDataFromFieldCollection(Item item) { CheckboxField checkboxField = new CheckboxField(item.Fields[MY_CHECKBOX_FIELD]); if (checkboxField.Checked) { TextField textField = new TextField(item.Fields[MY_TEXT_FIELD]); string textValue = textField.Value; if (!string.IsNullOrEmpty(textValue)) return textValue; return MISSING_TEXT; } return string.Empty; } public string RunWithDataFromBaseCustomItemIndex(Item item) { bool isChecked = item[MY_CHECKBOX_FIELD] == "1"; if (isChecked) { string textValue = item[MY_TEXT_FIELD]; if (!string.IsNullOrEmpty(textValue)) return textValue; return MISSING_TEXT; } return string.Empty; } }
First we look at the code that uses the Field[], This example is much like using the TestItem if used in earlier post found here, or at least the setup is. You need to setup a fieldcollection and ensure that the fields used exists in the collection.
[TestClass] public class TestFieldAccess { private CodeSnippet _codeSnippet; [TestInitialize] public void Initialize() { _codeSnippet = new CodeSnippet(); } [TestCleanup] public void CleanUp() { _codeSnippet = null; } [TestMethod] public void Run_WithCheckboxFieldNotChecked_ShouldReturnEmptyString() { using (ShimsContext.Create()) { Field checkBoxField = new ShimField() { IDGet = () => CodeSnippet.MY_CHECKBOX_FIELD, ValueGet = () => "0" }; FieldCollection fieldCollection = new ShimFieldCollection() { ItemGetID = id => checkBoxField }; Item itemStub = new ShimItem() { FieldsGet = () => fieldCollection}; string actualText = _codeSnippet.RunWithDataFromFieldCollection(itemStub); Assert.AreSame(string.Empty, actualText); } } [TestMethod] public void Run_WithCheckboxFieldCheckedButNotTextInTextField_ShouldReturnMissingText() { using (ShimsContext.Create()) { Field checkBoxField = new ShimField() { IDGet = () => CodeSnippet.MY_CHECKBOX_FIELD, ValueGet = () => "1" }; Field textField = new ShimField() { IDGet = () => CodeSnippet.MY_TEXT_FIELD, ValueGet = () =>string.Empty }; FieldCollection fieldCollection = new ShimFieldCollection() { ItemGetID = id => id==CodeSnippet.MY_CHECKBOX_FIELD ?checkBoxField : id == CodeSnippet.MY_TEXT_FIELD ? textField : null }; Item itemStub = new ShimItem() { FieldsGet = () => fieldCollection }; string actualText = _codeSnippet.RunWithDataFromFieldCollection(itemStub); Assert.AreSame(CodeSnippet.MISSING_TEXT, actualText); } } [TestMethod] public void Run_WithCheckboxFieldCheckedButNotTextInTextField_ShouldReturnActualTextFromTextField() { using (ShimsContext.Create()) { string expectedText = " Should return this string"; Field checkBoxField = new ShimField() { IDGet = () => CodeSnippet.MY_CHECKBOX_FIELD, ValueGet = () => "1" }; Field textField = new ShimField() { IDGet = () => CodeSnippet.MY_TEXT_FIELD, ValueGet = () => expectedText }; FieldCollection fieldCollection = new ShimFieldCollection() { ItemGetID = id => id == CodeSnippet.MY_CHECKBOX_FIELD ? checkBoxField : id == CodeSnippet.MY_TEXT_FIELD ? textField : null }; Item itemStub = new ShimItem() { FieldsGet = () => fieldCollection }; string actualText = _codeSnippet.RunWithDataFromFieldCollection(itemStub); Assert.AreSame(expectedText, actualText); } } }
and the result off course 3 green bars with 100 % test coverage.
And yes there is a lot of setup.
Now lets look at the indexing, here it becomes a little tricky since the derived BaseItem public methods isn’t overwritten on a ShimItem there for we will have to fake calls to BaseItem instead of the Item but still we need to create a stub of the item with ShimItem.
[TestClass] public class TestIndexAccess { private CodeSnippet _codeSnippet; [TestInitialize] public void Initialize() { _codeSnippet = new CodeSnippet(); } [TestCleanup] public void CleanUp() { _codeSnippet = null; } [TestMethod] public void Run_WithCheckboxFieldNotChecked_ShouldReturnEmptyString() { using (ShimsContext.Create()) { ShimBaseItem.AllInstances.ItemGetID = (item, id) => "0"; Item itemStub = new ShimItem(); string actualText = _codeSnippet.RunWithDataFromBaseCustomItemIndex(itemStub); Assert.AreSame(string.Empty, actualText); } } [TestMethod] public void Run_WithCheckboxFieldCheckedButNotTextInTextField_ShouldReturnMissingText() { using (ShimsContext.Create()) { ShimBaseItem.AllInstances.ItemGetID = (item, id) => id == CodeSnippet.MY_CHECKBOX_FIELD ? "1" : id == CodeSnippet.MY_TEXT_FIELD ? string.Empty : null; Item itemStub = new ShimItem(); string actualText = _codeSnippet.RunWithDataFromBaseCustomItemIndex(itemStub); Assert.AreSame(CodeSnippet.MISSING_TEXT, actualText); } } [TestMethod] public void Run_WithCheckboxFieldCheckedButNotTextInTextField_ShouldReturnActualTextFromTextField() { using (ShimsContext.Create()) { string expectedText = " Should return this string"; ShimBaseItem.AllInstances.ItemGetID = (item, id) => id == CodeSnippet.MY_CHECKBOX_FIELD ? "1" : id == CodeSnippet.MY_TEXT_FIELD ? expectedText : null; Item itemStub = new ShimItem(); string actualText = _codeSnippet.RunWithDataFromBaseCustomItemIndex(itemStub); Assert.AreSame(expectedText, actualText); } } } <pre>
And once again the result off course 3 green bars with 100 % test coverage.
It might no be as smooth as Typemock mocking framework, and yes demands a bit more setup, at least at to begin with.
Hello,
When I add the Sitecore Fakes (on the sitecore 7 dll) I have the error message: error while loading the assembly) do you have an idea why?
Regards,
BEnjamin Vangansewinkel
Try and add a reference for the Sitecore.Nexus dll as well. when added try and recreate the sitecore.kernel fakes assembly.
Regards
Thomas Stern
I get an error in Sitecore.Fakes.Tests.SecurityAndEditTests.CreateAndEditItemTest:
Could not find configuration node: clientDataStore
Should I add the clientDataStore to the app.config? With what data?
Can you please verify that app.config file in your test project is identical with the one found on github here https://github.com/istern/Sitecore-Fakes/blob/master/Tests/app.config Let me know
Yes, I have the same app.config. Shouldn’t there be a node in the config? Every Sitecore installation needs this.
Hmm Sounds strange I’ve just tried downloading Sitecore.Fakes from Github and fixing the missing reference for Sitecore.Kernel and Sitecore.nexus and the test passes could you try to download it again from github https://github.com/istern/Sitecore-Fakes or supply a path to a zip file with your code so i can try to recreate the problem ?
Also i guess your comment is on the wrong blog post since you list Sitecore.Fakes.Tests.SecurityAndEditTests.CreateAndEditItemTest Which is not related to Microsoft fakes. 🙂 let me know if get it to work..