通过REST API将测试结果发布到TFS 2018测试用例

时间:2019-01-30 14:57:20

标签: c# .net rest tfs automated-tests

当前,我们正在使用mstest.exe运行我们的自动化测试,然后会创建一个.trx结果文件。然后,随后,我们使用tcm.exe将这些结果发布到TFS服务器上的一些测试套件/测试用例中。

我们希望不再使用tcm,而使用TFS REST API从.trx结果文件中发布结果。

我已经阅读了the documentation on the REST API的相关内容,但是使用TFS扩展客户端类(例如TestManagementHttpClient)并没有什么特别的,它只列举了要使用的实际URL。它还没有给出关于期望参数的示例。

Microsoft.TeamFoundation.TestManagement.WebApi命名空间有一个官方的.NET reference documentation,该命名空间有所帮助,但它又没有任何示例/示例来知道每个函数期望的参数。

only example/sample I was able to find不够详尽,不足以让我理解如何在我的情况下应用它,因为我对测试点/测试运行的概念还不够熟悉,无法操作代表它们的类。

我猜一个测试运行有多个测试点(每个测试用例一个?)代表执行该测试用例的结果?在这种情况下,我应该为每个测试结果创建一个测试点。如果是这样,我怎么知道要提供哪个ID?上面的示例将“ 3”作为其硬编码值。

如果有人可以解释上述示例,并提供与我的用例相关的更好/更完整的示例(来自.trx文件,然后将这些结果发布到与某个测试套件下的链接的自动化项目匹配的测试用例中) ),可以帮助我了解所有事物之间如何相互关联,我将不胜感激。

谢谢。

1 个答案:

答案 0 :(得分:1)

因此,根据我在问题中链接的示例/示例来回答我自己的问题:

  1. 您需要使用TestManagementHttpClient.GetTestConfigurationsAsync()

  2. 获得所需的测试配置。
  3. 然后,您想使用TestManagementHttpClient.GetPointsAsync()

  4. 来获取该测试用例/测试配置组合的所有测试点。
  5. 然后您需要创建一个测试运行。这是通过至少至少声明您先前获取的测试点ID来声明一个新的RunCreateModel对象来完成的。您可能还需要填写很多参数(buildIdisAutomated等)。然后,您需要调用TestManagementHttpClient.CreateTestRunAsync()才能真正创建它。

  6. 对于您在创建测试点时指定的每个测试点,第3步实际上在测试运行下创建了空测试结果。您需要使用TestManagementHttpClient.GetTestResultsAsync()获取它们,并使用Outcome属性修改它们的TestCaseResult.TestCase.Id属性,以了解哪个结果适用于哪个测试用例。您可能还需要填写其他属性,例如State等。同样,您需要使用TestManagementHttpClient.UpdateTestResultsAsync()

  7. 将这些修改推送到TFS
  8. 最后一步是通过使用RunUpdateModel创建一个state = "Completed"对象,然后调用TestManagementHttpClient.UpdateTestRunAsync()

  9. ,将测试运行设置为已完成。

这是我最终编写的功能,它用F#编写:

// A test point is a pairing of a test case with a test configuration
let createTestRun (httpClient:TestManagementHttpClient) (testRunName:string) (teamProjectName:string) (testPlanId:int) (testSuiteId:int) (testCaseIdsAndResults:seq<(int * string)>) (buildId:int) (cancellationToken:CancellationToken) = async {
    let testPlanIdString = testPlanId.ToString()
    let plan = new ShallowReference(testPlanIdString)

    let! testConfigurations = httpClient.GetTestConfigurationsAsync(teamProjectName, cancellationToken = cancellationToken) |> Async.AwaitTask
    let defaultTestConfiguration = testConfigurations |> Seq.find (fun c -> c.IsDefault) // TODO: We only use the default configuration for now. Do we always want this?

    let rec getTestPoints (testIdsAndResults:(int * string) list) (testPoints:TestPoint[]) = async {
        match testIdsAndResults with
        | (testId, _)::rest ->
            let! fetchedTestPoints = httpClient.GetPointsAsync(teamProjectName, testPlanId, testSuiteId, testCaseId = testId.ToString(), cancellationToken = cancellationToken) |> Async.AwaitTask
            let testPoint = fetchedTestPoints |> Seq.find (fun p -> p.Configuration.Id = defaultTestConfiguration.Id.ToString())
            let newTestPointsList = Array.append testPoints [|testPoint|]
            return! getTestPoints rest newTestPointsList
        | _ ->
            return testPoints
    }
    let! testPoints = getTestPoints (List.ofSeq testCaseIdsAndResults) Array.empty
    let testPointIds = testPoints |> Array.map (fun p -> p.Id)

    let runCreateModel = new RunCreateModel(name = testRunName, plan = plan, buildId = buildId, isAutomated = new Nullable<bool>(true), pointIds = testPointIds)
    let! testRun = httpClient.CreateTestRunAsync(runCreateModel, teamProjectName, cancellationToken = cancellationToken) |> Async.AwaitTask
    let! emptyResults = httpClient.GetTestResultsAsync(project = teamProjectName, runId = testRun.Id, outcomes = new List<TestOutcome>(), cancellationToken = cancellationToken) |> Async.AwaitTask

    let rec createCaseResults (testIdsAndResults:(int * string) list) (results:TestCaseResult[]) = async {
        match testIdsAndResults with
        | (testId, testResult)::rest ->
            let caseResult = emptyResults |> Seq.find (fun r -> r.TestCase.Id = testId.ToString())
            caseResult.State <- "Completed"
            caseResult.Outcome <- testResult // "passed", "failed", "never run", "not applicable"
            let newResultsList = Array.append results [|caseResult|]
            return! createCaseResults rest newResultsList
        | _ ->
            return results
    }
    let! results = createCaseResults (List.ofSeq testCaseIdsAndResults) Array.empty
    let! _ = httpClient.UpdateTestResultsAsync(results, teamProjectName, testRun.Id, cancellationToken = cancellationToken) |> Async.AwaitTask

    let runmodel = new RunUpdateModel(state = "Completed");
    let! _ = httpClient.UpdateTestRunAsync(runmodel, teamProjectName, testRun.Id, cancellationToken = cancellationToken) |> Async.AwaitTask

    ()
}