From 2c6537b072da965a5e721f89978ee9add6483662 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 10 Sep 2025 21:29:44 +0200 Subject: [PATCH] feat: added UI and Unit Tests --- QuizAppTests/QuizAppTests.swift | 17 --- QuizAppUITests/QuizAppUITests.swift | 61 ++++++---- .../QuizAppUITestsLaunchTests.swift | 33 ------ QuizAppUnitTests/QuizAppUnitTests.swift | 107 ++++++++++++++++++ 4 files changed, 149 insertions(+), 69 deletions(-) delete mode 100644 QuizAppTests/QuizAppTests.swift delete mode 100644 QuizAppUITests/QuizAppUITestsLaunchTests.swift create mode 100644 QuizAppUnitTests/QuizAppUnitTests.swift diff --git a/QuizAppTests/QuizAppTests.swift b/QuizAppTests/QuizAppTests.swift deleted file mode 100644 index 5730e4f..0000000 --- a/QuizAppTests/QuizAppTests.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// QuizAppTests.swift -// QuizAppTests -// -// Created by Paul Nowotka on 02.08.25. -// - -import Testing -@testable import QuizApp - -struct QuizAppTests { - - @Test func example() async throws { - // Write your test here and use APIs like `#expect(...)` to check expected conditions. - } - -} diff --git a/QuizAppUITests/QuizAppUITests.swift b/QuizAppUITests/QuizAppUITests.swift index 493e37d..c69b950 100644 --- a/QuizAppUITests/QuizAppUITests.swift +++ b/QuizAppUITests/QuizAppUITests.swift @@ -2,40 +2,63 @@ // QuizAppUITests.swift // QuizAppUITests // -// Created by Paul Nowotka on 02.08.25. +// Created by Paul on 02.08.25. // import XCTest final class QuizAppUITests: XCTestCase { - + var app: XCUIApplication! + override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + app = XCUIApplication() + app.launch() } override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. } - @MainActor - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() + func testMultipleChoice() throws { + app/*@START_MENU_TOKEN@*/.buttons.containing(.staticText, identifier: "Hauptstädte").firstMatch/*[[".buttons",".containing(.staticText, identifier: \"Highscore: 7\").firstMatch",".containing(.staticText, identifier: \"Hauptstädte\").firstMatch",".otherElements.buttons[\"Hauptstädte, Highscore: 7\"]",".buttons[\"Hauptstädte, Highscore: 7\"]"],[[[-1,4],[-1,3],[-1,0,1]],[[-1,2],[-1,1]]],[2,0]]@END_MENU_TOKEN@*/.tap() + + //Erscheint die erste Frage korrekt? + let q1 = app.staticTexts["Was ist die Hauptstadt von Frankreich?"] + XCTAssertTrue(q1.waitForExistence(timeout: 1)) + + app.buttons["Paris"].tap() + + //Korrekte Auswertung? + XCTAssertTrue(app.staticTexts["✅ Richtig!"].waitForExistence(timeout: 1)) + XCTAssertTrue(app.buttons["Nächste Frage"].exists) + + } + + func testEstimationSliderDisabledAfterSubmit() throws { + app.staticTexts["Schätzen"].tap() - // Use XCTAssert and related functions to verify your tests produce the correct results. + let slider = app.sliders.element(boundBy: 0) + XCTAssertTrue(slider.waitForExistence(timeout: 2)) + slider.adjust(toNormalizedSliderPosition: 0.7) + + let submit = app.buttons["Antwort abgeben"] + XCTAssertTrue(submit.exists) + submit.tap() + + // Slider muss disabled sein + XCTAssertFalse(slider.isEnabled) + + XCTAssertTrue(app.buttons["Nächste Frage"].waitForExistence(timeout: 2)) } - @MainActor - func testLaunchPerformance() throws { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } + + func testQuit() throws { + app/*@START_MENU_TOKEN@*/.buttons.containing(.staticText, identifier: "Hauptstädte").firstMatch/*[[".buttons",".containing(.staticText, identifier: \"Highscore: 7\").firstMatch",".containing(.staticText, identifier: \"Hauptstädte\").firstMatch",".otherElements.buttons[\"Hauptstädte, Highscore: 7\"]",".buttons[\"Hauptstädte, Highscore: 7\"]"],[[[-1,4],[-1,3],[-1,0,1]],[[-1,2],[-1,1]]],[2,0]]@END_MENU_TOKEN@*/.tap() + app/*@START_MENU_TOKEN@*/.buttons["Abbrechen"]/*[[".otherElements[\"Abbrechen\"].buttons.firstMatch",".otherElements.buttons[\"Abbrechen\"]",".buttons[\"Abbrechen\"]"],[[[-1,2],[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap() + app/*@START_MENU_TOKEN@*/.buttons["Quiz verlassen"]/*[[".otherElements.buttons[\"Quiz verlassen\"]",".buttons[\"Quiz verlassen\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap() + let welcome_msg = app.staticTexts["Willkommen!"] + XCTAssertTrue(welcome_msg.waitForExistence(timeout: 1)) + } } diff --git a/QuizAppUITests/QuizAppUITestsLaunchTests.swift b/QuizAppUITests/QuizAppUITestsLaunchTests.swift deleted file mode 100644 index b1d4f8e..0000000 --- a/QuizAppUITests/QuizAppUITestsLaunchTests.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// QuizAppUITestsLaunchTests.swift -// QuizAppUITests -// -// Created by Paul Nowotka on 02.08.25. -// - -import XCTest - -final class QuizAppUITestsLaunchTests: XCTestCase { - - override class var runsForEachTargetApplicationUIConfiguration: Bool { - true - } - - override func setUpWithError() throws { - continueAfterFailure = false - } - - @MainActor - func testLaunch() throws { - let app = XCUIApplication() - app.launch() - - // Insert steps here to perform after app launch but before taking a screenshot, - // such as logging into a test account or navigating somewhere in the app - - let attachment = XCTAttachment(screenshot: app.screenshot()) - attachment.name = "Launch Screen" - attachment.lifetime = .keepAlways - add(attachment) - } -} diff --git a/QuizAppUnitTests/QuizAppUnitTests.swift b/QuizAppUnitTests/QuizAppUnitTests.swift new file mode 100644 index 0000000..9320231 --- /dev/null +++ b/QuizAppUnitTests/QuizAppUnitTests.swift @@ -0,0 +1,107 @@ +// +// QuizAppUnitTests.swift +// QuizAppUnitTests +// +// Created by Paul on 08.09.25. +// + +import XCTest + +@testable import QuizApp + +final class QuizAppUnitTests: XCTestCase { + + private func makeMC( + question: String, + answers: [String] = ["A", "B", "C"], + correct: Int + ) -> QuizQuestion { + struct Payload: Encodable { + let question: String + let answers: [String] + let correctAnswer: Int + } + let payload = Payload( + question: question, + answers: answers, + correctAnswer: correct + ) + let data = try! JSONEncoder().encode(payload) + return try! JSONDecoder().decode(QuizQuestion.self, from: data) + } + + private func makeEst( + question: String, + correct: Double, + min: Double = 0, + max: Double = 100, + unit: String = "" + ) -> QuizQuestion { + struct Payload: Encodable { + let type = "estimation" + let question: String + let minValue: Double + let maxValue: Double + let correctValue: Double + let unit: String + } + let payload = Payload( + question: question, + minValue: min, + maxValue: max, + correctValue: correct, + unit: unit + ) + let data = try! JSONEncoder().encode(payload) + return try! JSONDecoder().decode(QuizQuestion.self, from: data) + } + + func testIncrementScore_CorrectAnswer() { + let vm = QuizViewModel() + vm.questions = [makeMC(question: "Q1", correct: 2)] + vm.currentQuestionIndex = 0 + XCTAssertEqual(vm.score, 0) + vm.incrementScore(selectedIndex: 2) + XCTAssertEqual(vm.score, 1) + } + + func testIncrementScore_WrongAnswer() { + let vm = QuizViewModel() + vm.questions = [makeMC(question: "Q1", correct: 2)] + vm.currentQuestionIndex = 0 + vm.incrementScore(selectedIndex: 1) + XCTAssertEqual(vm.score, 0) + } + + func testLoadNextQuestion_NotFinished() { + let vm = QuizViewModel() + vm.questions = [ + makeMC(question: "Q1", correct: 0), + makeMC(question: "Q2", correct: 0), + ] + vm.currentQuestionIndex = 0 + vm.loadNextQuestion() + XCTAssertFalse(vm.isQuizFinished) + } + + func testLoadNextQuestion_Finished() { + let vm = QuizViewModel() + vm.questions = [makeMC(question: "Q1", correct: 0)] + vm.currentQuestionIndex = 0 + vm.loadNextQuestion() + XCTAssertTrue(vm.isQuizFinished) + } + + func testPointsForEstimation_onePoint() { + let vm = QuizViewModel() + let p = vm.pointsForEstimation(guess: 109, correct: 100, maxPoints: 1) + XCTAssertEqual(p, 1) + } + + func testPointsForEstimation_ZeroPoints() { + let vm = QuizViewModel() + let p = vm.pointsForEstimation(guess: 111, correct: 100, maxPoints: 1) + XCTAssertEqual(p, 0) + } + +}