{"id":3883,"date":"2012-11-26T08:34:00","date_gmt":"2012-11-26T08:34:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2012\/11\/26\/introducing-sharepoint-emulators\/"},"modified":"2022-07-21T05:49:56","modified_gmt":"2022-07-21T13:49:56","slug":"introducing-sharepoint-emulators","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/devops\/introducing-sharepoint-emulators\/","title":{"rendered":"Introducing SharePoint Emulators"},"content":{"rendered":"<p>The Visual Studio team is pleased to announce availability of Visual Studio 2012 SharePoint Emulators. Unit testing for SharePoint code is difficult. Ideally you would like to isolate code under test from the surrounding SharePoint framework. Isolation frameworks, such as Microsoft Fakes, can enable this code isolation. Unfortunately, as anyone who has followed this path will tell you, creating and maintaining the set of mocks and fakes for the SharePoint environment can be very costly. Luckily, the SharePoint Emulators team has done the heavy lifting for you. SharePoint Emulators provide a system of Fakes based shims implementing the basic behaviors of the SharePoint 2010 server object model.<\/p>\n<h2>System Requirements<\/h2>\n<p>\u00b7 Microsoft SharePoint 2010 Server (SharePoint 2010 Server or SharePoint 2010 Foundation)<\/p>\n<p>\u00b7 Microsoft Visual Studio 2012 Ultimate<\/p>\n<h2>Getting SharePoint Emulators<\/h2>\n<p>SharePoint Emulators are available today through a NuGet package feed. Perform a simple search of the NuGet official feed for \u201cSharePoint Emulators\u201d.<\/p>\n<p><img decoding=\"async\" title=\"clip_image001\" border=\"0\" alt=\"clip_image001\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/11\/1200.clip_image001_thumb_77CBFDFB.png\" width=\"506\" height=\"132\" \/><\/p>\n<h2>Using SharePoint Emulators<\/h2>\n<p>SharePoint Emulators are easy to incorporate into existing tests. You can write tests against the SharePoint Emulators using only the SharePoint API. All that is needed is the wrapping of test code in a SharePointEmulationScope.<\/p>\n<p><font face=\"Courier New\">[TestMethod]\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>public\u00a0 void ScheduleAppointmentReturnsTrueWhenNewAppointmentIsCreated()\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>{\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 <strong><font color=\"#ff0000\">using (new SharePointEmulationScope(EmulationMode.Enabled))<\/font><\/strong>\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 <strong><font color=\"#ff0000\">{<\/font><\/strong>\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SPSite site = new SPSite(&#8220;<\/font><font face=\"Courier New\">http:\/\/localhost&#8221;);<\/font>\u00a0\u00a0\u00a0<br \/>\n<font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 string listName = String.Format(&#8220;List{0}&#8221;, Guid.NewGuid());<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ create a new temporary list\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Guid listId = site.RootWeb.Lists.Add listName, listName,\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SPListTemplateType.GenericList);<\/font><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/font><font face=\"Courier New\">SPList list = site.RootWeb.Lists[listId];\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Assert.IsNotNull(list);<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ add fields to list\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Name&#8221;, SPFieldType.Text, true);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Phone&#8221;, SPFieldType.Text, false);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Email&#8221;, SPFieldType.Text, false);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Age&#8221;, SPFieldType.Text, false);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Date&#8221;, SPFieldType.Text, false); <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Update();<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ prepare <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 string errorMsg = string.Empty; <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 DateTime date = DateTime.Now; <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 BookAnAppointmentWebPart webPart = new BookAnAppointmentWebPart();<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ act <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 bool success = webPart.ScheduleAppointment(site.RootWeb, list.Title, &#8220;My Name&#8221;, <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;888-888-8888&#8221;, &#8220;My.Name@contoso.com&#8221;, &#8220;23&#8221;, date, out errorMsg);<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ assert <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Assert.IsTrue(success);<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ cleanup <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Delete(); <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 site.Dispose(); <br \/>\u00a0\u00a0\u00a0 <\/font><font color=\"#ff0000\"><strong>}<\/strong><\/font><br \/>\n}<\/p>\n<p>All code wrapped within a SharePointEmulationScope will be automatically detoured at runtime. All calls against the Microsoft.SharePoint.dll assembly will be rerouted to the SharePoint Emulators.<\/p>\n<h2>Extending SharePoint Emulators<\/h2>\n<p>Due to the wide size and scope of the SharePoint API surface we focused on the types and methods most highly used. Using a type that has not been implemented results in a little bit of extra work to get the test passing.\u00a0 As an example the following test fails, with an unimplemented exception.<\/p>\n<p><font face=\"Courier New\">[TestMethod] <br \/> public void GetAppointmentsForTodayReturnsTwoAppointments() <br \/> {\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 using (var emulationScope = new SharePointEmulationScope(EmulationMode.Enabled))\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 {\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SPSite site = new SPSite(&#8220;<\/font><font face=\"Courier New\">http:\/\/localhost&#8221;);<\/font>\u00a0\u00a0<br \/>\n<font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 string listName = String.Format(&#8220;List{0}&#8221;, Guid.NewGuid());<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ create a new temporary list\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Guid listId = site.RootWeb.Lists.Add(listName, listName,\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SPListTemplateType.GenericList);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SPList list = site.RootWeb.Lists[listId];\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Assert.IsNotNull(list);<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ add fields to list\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Name&#8221;, SPFieldType.Text, true);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Phone&#8221;, SPFieldType.Text, false);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Email&#8221;, SPFieldType.Text, false);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Age&#8221;, SPFieldType.Text, false);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Fields.Add(&#8220;Date&#8221;, SPFieldType.Text, false);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Update();<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 DateTime date = DateTime.Now;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 BookAnAppointmentWebPart webPart = new BookAnAppointmentWebPart();<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ insert 3 item into list\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SPItem item = list.Items.Add();\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item[&#8220;Name&#8221;] = &#8220;My Name&#8221;;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item[&#8220;Phone&#8221;] = &#8220;888-888-8888&#8221;;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item[&#8220;Email&#8221;] = &#8220;My.Name@contoso.com&#8221;;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item[&#8220;Age&#8221;] = &#8220;23&#8221;;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item[&#8220;Date&#8221;] = date.ToString(&#8220;D&#8221;);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item.Update();<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SPItem item2 = list.Items.Add();\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item2[&#8220;Name&#8221;] = &#8220;His Name&#8221;;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item2[&#8220;Phone&#8221;] = &#8220;777-777-7777&#8221;;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item2[&#8220;Email&#8221;] = &#8220;His.Name@contoso.com&#8221;;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item2[&#8220;Age&#8221;] = &#8220;25&#8221;;\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item2[&#8220;Date&#8221;] = date.AddDays(1).ToString(&#8220;D&#8221;);\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 item2.Update();<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Act\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 string result = webPart.GetAppointmentsForToday(list.Title, site.RootWeb);<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Assert\u00a0\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Assert.IsTrue(result.Contains(String.Format(&#8220;Name: My Name, Phone: 888-888-8888, <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Email: My.Name@contoso.com, Age: 23, Date: {0}&#8221;, date.ToString(&#8220;D&#8221;))));\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Assert.IsFalse(result.Contains(&#8220;Name: His Name&#8221;));<\/font><\/p>\n<p><font face=\"Courier New\"><\/font><\/p>\n<p><font face=\"Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ cleanup\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list.Delete();\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 site.Dispose();\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 }\u00a0\u00a0\u00a0\u00a0 <br \/>}<\/font><\/p>\n<p>\u00a0<img decoding=\"async\" title=\"clip_image003\" border=\"0\" alt=\"clip_image003\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/11\/7651.clip_image003_thumb_3795E481.png\" width=\"684\" height=\"123\" \/><\/p>\n<pre>&#160;<\/pre>\n<p>If you do find an area not covered, it is straightforward to extend and implement new emulated behavior. SharePoint Emulators rely entirely on the Microsoft Fakes framework. They consist of only concrete implementations of shims. When encountering an unimplemented shim all that is needed is to provide a implementation of that shim. The details of the shim that is required is detailed in the error message, in this example SPList.GetItems(SPQuery).<\/p>\n<p><font face=\"Courier New\">if (emulationScope.EmulationMode == EmulationMode.Enabled)\u00a0\u00a0\u00a0\u00a0 <br \/>{\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 var sList = new ShimSPList(list);\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 sList.GetItemsSPQuery = (query) =>\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 {\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var shim = new ShimSPListItemCollection();\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 shim.Bind(new[] { list.Items[0] });\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return shim.Instance;\u00a0\u00a0\u00a0\u00a0 <br \/>\u00a0\u00a0\u00a0 };\u00a0\u00a0\u00a0\u00a0 <br \/>}<\/font><\/p>\n<p>The addition of this code to the previous test allows it to pass. You can use this technique to implement missing behavior or to customize Emulators to match your own environment.<\/p>\n<h2>Running Multi-Target Tests<\/h2>\n<p>Running tests against the Emulators provides many advantages: performance improvements, isolation from SharePoint, and reduced maintenance of test SharePoint infrastructure. However, there are times when you still want to run tests against a real SharePoint server. SharePoint emulators enables the reuse of the same test code, but run in different context.<\/p>\n<p>The SharePointEmulationScope takes as a parameter an optional EmulationMode enum. The default enabled mode performs run time interception of all Microsoft.SharePoint.dll calls. The second passthrough value allows all calls to bypass all shims and directly call into the original assembly. The use of this enum value can control the context of test execution.<\/p>\n<p>There are numerous ways of consuming and controlling this EmulationMode value. The SharePoint emulator team is fond of Data Driven tests to control the EmulationMode. In this way the test team is able to write tests that work against the SharePoint Emulators and the default SharePoint server. We use these tests and this approach to verify the behavior of the SharePoint Emulators. There are lots of alternatives for usage: setup and initialization code, preprocessor directives, ect.<\/p>\n<pre>&#160;<\/pre>\n<h2>Resources<\/h2>\n<ul>\n<li><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/jj822928.aspx\">MSDN SharePoint Emulators Docs (soon to be live)<\/a><\/li>\n<li><a href=\"http:\/\/nuget.org\/packages\/Microsoft.SharePoint.Emulators\">SharePoint Emulators NuGet Package<\/a><\/li>\n<li><a href=\"https:\/\/channel9.msdn.com\/Events\/Build\/2012\/3-015\">\/\/Build\/ 2012 Talk on Fakes and Emulators<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The Visual Studio team is pleased to announce availability of Visual Studio 2012 SharePoint Emulators. Unit testing for SharePoint code is difficult. Ideally you would like to isolate code under test from the surrounding SharePoint framework. Isolation frameworks, such as Microsoft Fakes, can enable this code isolation. Unfortunately, as anyone who has followed this path [&hellip;]<\/p>\n","protected":false},"author":100,"featured_media":45953,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,252],"tags":[],"class_list":["post-3883","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-testing"],"acf":[],"blog_post_summary":"<p>The Visual Studio team is pleased to announce availability of Visual Studio 2012 SharePoint Emulators. Unit testing for SharePoint code is difficult. Ideally you would like to isolate code under test from the surrounding SharePoint framework. Isolation frameworks, such as Microsoft Fakes, can enable this code isolation. Unfortunately, as anyone who has followed this path [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/3883","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/users\/100"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/comments?post=3883"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/3883\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media\/45953"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media?parent=3883"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/categories?post=3883"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/tags?post=3883"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}