{"id":4925,"date":"2016-12-19T17:29:55","date_gmt":"2016-12-19T17:29:55","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/premier_developer\/?p=4925"},"modified":"2019-02-14T20:27:47","modified_gmt":"2019-02-15T03:27:47","slug":"rollback-tests-with-tfsvsts","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/rollback-tests-with-tfsvsts\/","title":{"rendered":"Rollback tests with TFS\/VSTS"},"content":{"rendered":"<p>In this post, Premier ADM <a href=\"https:\/\/ca.linkedin.com\/in\/mikelapierre\">Mike Lapierre<\/a> covers techniques to rollback bulk editing changes with TFS test cases.<\/p>\n<hr width=\"100%\" size=\"1\" align=\"center\" \/>\n<p>I was assisting a customer recently who was working on many TFS test cases, using Excel for bulk editing.\u00a0 The Excel integration is great for editing regular fields but it can\u2019t handle HTML fields like the description or the test steps.\u00a0 When such a field is modified, the following message is displayed.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/31\/2019\/04\/rollback1.jpg\"><img decoding=\"async\" width=\"644\" height=\"343\" title=\"rollback1\" style=\"padding-top: 0px;padding-left: 0px;padding-right: 0px;border-width: 0px\" alt=\"rollback1\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/31\/2019\/04\/rollback1_thumb.jpg\" border=\"0\" \/><\/a><\/p>\n<p>We all take time to read such messages, right?\u00a0 Okay, maybe not <em>all the time<\/em>.\u00a0 Once you publish the test cases and the damage is done, how can you get back to the previous state?\u00a0 The <em>short answer<\/em> is there\u2019s no rollback mechanism built in, so you must get the previous values from the history, put them back in, and save the test case.\u00a0 Furthermore, test steps are all stored in a single field so you must split them back for the UI.\u00a0 This might be a viable solution for a few test cases, but if we\u2019re talking about hundreds of test cases, it\u2019s not.<\/p>\n<p>So, what\u2019s the <em>long answer<\/em>?\u00a0 We can use the TFS APIs to get the values from a previous revision and put them back in the test case before saving.\u00a0 The solution is the same, but it\u2019s automated and scales well to any number of test cases.<\/p>\n<p>I chose to use PowerShell to build the script, but this can also be done from any .NET application in the language of your choosing.\u00a0 For starters, we need to load the required TFS APIs in the PowerShell session.\u00a0 I am using Visual Studio \/ Team Explorer 2015, so the APIs can be found in the folder below.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Courier New\">$AssemblyFolder = &#8220;${Env:ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\Common7\\IDE\\CommonExtensions\\Microsoft\\TeamFoundation\\Team Explorer&#8221;<\/span><\/p>\n<p><span style=\"font-family: Courier New\">[System.Reflection.Assembly]::LoadFrom(&#8220;$AssemblyFolder\\Microsoft.TeamFoundation.Client.dll&#8221;) <\/span><\/p>\n<p><span style=\"font-family: Courier New\">[System.Reflection.Assembly]::LoadFrom(&#8220;$AssemblyFolder\\Microsoft.TeamFoundation.TestManagement.Client.dll&#8221;)<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>The next step is to connect to the Team Project Collection and grab the Test Management service and the desired Team Project.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Courier New\">$TargetProject = &#8220;Team Project&#8221;<\/span><\/p>\n<p><span style=\"font-family: Courier New\">$TargetUrl = &#8220;<\/span><a href=\"http:\/\/host:port\/tfs\/collection\"><span style=\"font-family: Courier New\">http:\/\/host:port\/tfs\/collection<\/span><\/a><span style=\"font-family: Courier New\">&#8220;<\/span><\/p>\n<p><span style=\"font-family: Courier New\">$TargetTFS = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection(New-Object System.Uri($TargetUrl))<\/span><\/p>\n<p><span style=\"font-family: Courier New\">$TargetTestMgmt = $TargetTFS.GetService([Microsoft.TeamFoundation.TestManagement.Client.ITestManagementService])<\/span><\/p>\n<p><span style=\"font-family: Courier New\">$TargetTeamProject = $TargetTestMgmt.GetTeamProject($TargetProject)<\/span><\/p>\n<p><span style=\"font-family: Courier New\"><\/span><\/p>\n<p>We now need to locate the test cases we want to rollback.\u00a0 Test cases are basically work items with extra features on top.\u00a0 Their history \/ revision is handled by work items.\u00a0 To locate the work items, you can get fancy with the <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/bb130306.aspx\">WIQL language<\/a>, but the easiest way is to use IDs.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Courier New\">$TargetIds = &#8220;(1,2,3)&#8221;<\/span><\/p>\n<p><span style=\"font-family: Courier New\">$WorkItems = $TargetTeamProject.WitProject.Store.Query(&#8220;SELECT * FROM WorkItems WHERE [System.Id] IN $TargetIds&#8221;) <\/span><\/p>\n<p>&nbsp;<\/p>\n<p>Now we have the affected work items, we must spot which revision we want to restore for each work item.\u00a0 Again, you can use different methods for this, but let\u2019s use the \u201cBack to Future\u201d technique and set destination time to where we want to fix.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Courier New\">$TargetDate = Get-Date -Year 1985 -Month 10 -Day 26 -Hour 09 -Minute 00 -Second 00<\/span><\/p>\n<p><span style=\"font-family: Courier New\">$GoodRevision = $WorkItem.Revisions | ? { $_.Fields[&#8220;System.ChangedDate&#8221;].Value -lt $TargetDate } | select -Last 1<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>It\u2019s time to compare the last good revision with current revision. There\u2019s three possibilities here:<\/p>\n<ol>\n<li>There\u2019s no revision before the specified date: nothing to do, the test case did not exist.<\/li>\n<li>The current revision and the last good revision are the same: nothing to do, the test case has not been updated after the specified date.<\/li>\n<li>The current revision is different from the last good revision: we must restore the test case with the last good revision.<\/li>\n<\/ol>\n<p>Since we have to this for each work item, let\u2019s express it as a for loop (with a bit of logging).<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Courier New\">$TargetDate = Get-Date -Year 1985 -Month 10 -Day 26 -Hour 09 -Minute 00 -Second 00<\/span><\/p>\n<p><span style=\"font-family: Courier New\">foreach($WorkItem in $WorkItems)<\/span><\/p>\n<p><span style=\"font-family: Courier New\">{ <\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0 $GoodRevision = $WorkItem.Revisions | ? { $_.Fields[&#8220;System.ChangedDate&#8221;].Value -lt $TargetDate } | select -Last 1<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0 if ($GoodRevision)<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0 { <\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0 if ($WorkItem.Rev -eq $GoodRevision.Fields[&#8220;System.Rev&#8221;].Value)<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0 {<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Verbose &#8220;Test Case $($WorkItem.Id) has no revision after $TargetDate&#8221; <\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0 }<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0 else<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0 { <\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Verbose &#8220;Test Case $($WorkItem.Id) will be updated with revision $($GoodRevision.Fields[&#8220;System.Rev&#8221;].Value) ($($GoodRevision.Fields[&#8220;System.ChangedDate&#8221;].Value))&#8221;<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Restore the test case<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0 } <\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0 }<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0 else<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0 {<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Verbose &#8220;Test Case $($WorkItem.Id) has no revision before $TargetDate&#8221;<\/span><\/p>\n<p><span style=\"font-family: Courier New\">\u00a0\u00a0 } <\/span><\/p>\n<p><span style=\"font-family: Courier New\">} <\/span><\/p>\n<p><span style=\"font-family: Courier New\"><\/span><\/p>\n<p>The final step is to restore the test case with last good revision.\u00a0 You can choose the fields you want to restore.\u00a0 In this case, let\u2019s restore the description, steps, parameters, and their values.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Courier New\">$TestCase = $TargetTeamProject.TestCases.Find($WorkItem.Id)<\/span><\/p>\n<p><span style=\"font-family: Courier New\">$TestCase.Description = $GoodRevision.Fields[&#8220;System.Description&#8221;].Value<\/span><\/p>\n<p><span style=\"font-family: Courier New\">($TestCase.CustomFields | ? { $_.ReferenceName -eq &#8220;Microsoft.VSTS.TCM.Steps&#8221; }).Value = $GoodRevision.Fields[&#8220;Microsoft.VSTS.TCM.Steps&#8221;].Value<\/span><\/p>\n<p><span style=\"font-family: Courier New\">($TestCase.CustomFields | ? { $_.ReferenceName -eq &#8220;Microsoft.VSTS.TCM.Parameters&#8221; }).Value = $GoodRevision.Fields[&#8220;Microsoft.VSTS.TCM.Parameters&#8221;].Value<\/span><\/p>\n<p><span style=\"font-family: Courier New\">($TestCase.CustomFields | ? { $_.ReferenceName -eq &#8220;Microsoft.VSTS.TCM.LocalDataSource&#8221; }).Value = $GoodRevision.Fields[&#8220;Microsoft.VSTS.TCM.LocalDataSource&#8221;].Value<\/span><\/p>\n<p><span style=\"font-family: Courier New\">$TestCase.Save() <\/span><\/p>\n<p>&nbsp;<\/p>\n<p>That\u2019s it!\u00a0 We have successfully traveled through time to make sure we read the dialog box!\u00a0 You can find the full script on <a href=\"https:\/\/raw.githubusercontent.com\/mikelapierre\/tfs-tools\/master\/RollbackTests.ps1\">GitHub<\/a>.<\/p>\n<p>Note this technique can be used for regular work items too by using the Work Item API only instead of both the Work Item and Test Management APIs.<\/p>\n<p>If you want to run this against VSTS, you\u2019ll have to get a <a href=\"https:\/\/www.visualstudio.com\/en-us\/docs\/setup-admin\/team-services\/use-personal-access-tokens-to-authenticate\">Personal Access Token<\/a>, and add the following property assignment:<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Courier New\">$TargetTFS.ClientCredentials = New-Object Microsoft.TeamFoundation.Client.TfsClientCredentials(New-Object Microsoft.TeamFoundation.Client.BasicAuthCredential(New-Object System.Net.NetworkCredential(&#8220;&#8221;, &#8220;pat&#8221;)))<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>I hope this post helps you!<\/p>\n<hr \/>\n<p><a href=\"https:\/\/blogs.msdn.microsoft.com\/b\/premier_developer\/archive\/2014\/09\/15\/welcome.aspx\"><strong>Premier Support for Developers<\/strong><\/a> provides strategic technology guidance, critical support coverage, and a range of essential services to help teams optimize development lifecycles and improve software quality.\u00a0 Contact your Application Development Manager (ADM) or <a href=\"https:\/\/blogs.msdn.microsoft.com\/premier_developer\/contact-us\/\">email us<\/a> to learn more about what we can do for you.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, Premier ADM Mike Lapierre covers techniques to rollback bulk editing changes with TFS test cases. I was assisting a customer recently who was working on many TFS test cases, using Excel for bulk editing.\u00a0 The Excel integration is great for editing regular fields but it can\u2019t handle HTML fields like the description [&hellip;]<\/p>\n","protected":false},"author":582,"featured_media":37840,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[35],"tags":[3,359],"class_list":["post-4925","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-alm","tag-team","tag-team-foundation-server"],"acf":[],"blog_post_summary":"<p>In this post, Premier ADM Mike Lapierre covers techniques to rollback bulk editing changes with TFS test cases. I was assisting a customer recently who was working on many TFS test cases, using Excel for bulk editing.\u00a0 The Excel integration is great for editing regular fields but it can\u2019t handle HTML fields like the description [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/4925","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/582"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=4925"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/4925\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/37840"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=4925"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=4925"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=4925"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}