Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save denyszet/69475d9211d6787b0bddba2bd1314ed0 to your computer and use it in GitHub Desktop.
Save denyszet/69475d9211d6787b0bddba2bd1314ed0 to your computer and use it in GitHub Desktop.
Sample for pushing XCTest/XCUITest results to TestRail
//
// TestRailObservation.swift
//
import Foundation
import XCTest
class TestRailObservation: NSObject, XCTestObservation {
// Add a user / pass or key for testrail generation
var testrailUsername: String = ""
var testrailKey: String = ""
var testRun: Int? = nil
var testResults: [Result] = []
var title: String { get { return "iPhone iOS Ticketing Regression" } }
struct Result: Encodable {
var case_id: Int
var status_id: Int
var comment: String?
}
struct Results: Encodable {
var results: [Result]
}
struct TestrailResponse: Decodable {
var id: Int
var url: String
}
override init() {
super.init()
XCTestObservationCenter.shared.addTestObserver(self)
}
func testCaseDidFinish(_ testCase: XCTestCase) {
for id in self.parseTestCases(name: testCase.name) {
if (!self.testResults.contains(where: {$0.case_id == id})) {
testResults.append(Result(case_id: id, status_id: 1, comment: nil))
}
}
}
func testCase(_ testCase: XCTestCase, didRecord issue: XCTIssue) {
for id in self.parseTestCases(name: testCase.name) {
self.testResults.append(Result(case_id: id, status_id: 5, comment: issue.description))
}
}
private func parseTestCases(name: String) -> [Int] {
var testCaseIds: [Int] = []
let substrings = name.components(separatedBy: "_")
for substring in substrings {
if (substring.range(of: "^C\\d+$", options: .regularExpression) != nil) {
let testCaseId = Int(substring.dropFirst())!
testCaseIds.append(testCaseId)
}
}
return testCaseIds
}
private func createRequest(url: String) -> URLRequest {
let auth = String(format: "%@:%@", testrailUsername, testrailKey).data(using: String.Encoding.utf8)!.base64EncodedString()
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
request.setValue("Basic \(auth)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
return request
}
// This method provides a customization point for any post-testing activity. The test process will generally exit after this method returns, so if there is long running and/or asynchronous work to be done after testing, be sure to implement this method in a way that blocks until all long running activity is complete.
// -https://developer.apple.com/documentation/xctest/xctestobservation/1500819-testbundledidfinish
func testBundleDidFinish(_ testBundle: Bundle) {
self.testrailUsername = ProcessInfo.processInfo.environment["TESTRAIL_USERNAME"] ?? ""
self.testrailKey = ProcessInfo.processInfo.environment["TESTRAIL_KEY"] ?? ""
if (self.testrailUsername != "" && self.testrailKey != "") {
let semaphore = DispatchSemaphore(value: 0)
var addRunRequest = createRequest(url: "https://example.testrail.com/index.php?/api/v2/add_run/1")
addRunRequest.httpBody = Data("{\"suite_id\":1,\"name\":\"\(self.title)\"}".utf8)
let addRunTask = URLSession.shared.dataTask(with: addRunRequest) {(data, response, error) in
guard let data = data else { return }
do {
let addRunResult = try JSONDecoder().decode(TestrailResponse.self, from: data)
let testRun = addRunResult.id
let encoder = JSONEncoder()
let results = Results(results: self.testResults)
let encodedArray = try! encoder.encode(results)
var addResultsRequest = self.createRequest(url: "https://example.testrail.com/index.php?/api/v2/add_results_for_cases/\(testRun)")
addResultsRequest.httpBody = Data(encodedArray)
let addResultsTask = URLSession.shared.dataTask(with: addResultsRequest) {(data, response, error) in
semaphore.signal()
}
addResultsTask.resume()
} catch {
print(error)
}
}
addRunTask.resume()
semaphore.wait()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment