Wie kann ich Netzwerkantworten und -Antests für UI -Tests mit xCTest verspotten?IOS

Programmierung für iOS
Anonymous
 Wie kann ich Netzwerkantworten und -Antests für UI -Tests mit xCTest verspotten?

Post by Anonymous »

Für meine neue Swiftui -App möchte ich Tests hinzufügen. Mit Swift -Tests habe ich angefangen, einige Unit -Tests zu schreiben. Ich möchte auch UI -Tests hinzufügen. Die App stützt sich stark auf Netzwerkaufrufe, um Daten abzurufen, um Aktionen anzuzeigen und auszuführen. Status ist ebenfalls wichtig, die App ist nur nutzbar, wenn der Benutzer beispielsweise angemeldet ist. Bei Verwendung einiger Online -Tutorials habe ich einen einfachen UI -Test hinzugefügt, der die App öffnet und sich bei einem Benutzer anmeldet. In diesem Test wird die App tatsächlich ausgeführt und die Anmeldung durch Kontaktaufnahme mit einem tatsächlichen Authentifizierungsserver ausgeführt. Um andere Ansichten zu testen, müsste dies jedes Mal vor dem Ausführen des tatsächlichen Tests durchgeführt werden, um eine bestimmte Ansicht zu testen. Ich habe gesehen, dass es empfohlen wurde, Protokolle für die von den Ansichten verwendeten ViewModels zu verwenden und eine Scheinversion zu erstellen, damit die Vorschau funktionieren. Ist das die bevorzugte Methode, um Netzwerkanfragen zu verspotteten und für XCTest UI -Tests festzulegen? Wenn ja, wie sollten diese Mocks injiziert werden, wenn die UI-Tests die gesamte App und keine spezifischen Ansichten zu starten scheinen.

Code: Select all

let app = XCUIApplication()
app.launch()
App -Struktur
Unten ist ein vereinfachtes Beispiel für die Struktur meiner App. Für meine Tests würde ich gerne über den Isauthentized Status im AuthenticationModel verspottet und die Netzwerkaufrufe verspottet, entweder im AuthenticationModel oder im Apiclient .
Basierend auf den bisherigen Kommentaren und Antworten habe ich ein Protokoll eingeführt und eine grundlegende Form der Abhängigkeitsinjektion verwendet, um die Netzwerkschicht hinzuzufügen (

Code: Select all

APIClient
) zur AuthentifizierungModel . Dies sollte es mir ermöglichen, die Netzwerkschicht zu verspotten. Es ist mir jedoch unklar, wie ich die Mock-Implementierung im XCTest verwenden kann.

Code: Select all

import SwiftUI

struct ContentView: View {
@EnvironmentObject var authModel: AuthenticationModel

var body: some View {
if authModel.isAuthenticated {
VStack {
Text("Welcome \(authModel.username)")

Button {
Task {
await authModel.logout()
}
} label: {
Text("Logout")
}
.buttonStyle(.borderedProminent)
}
.padding()
} else {
VStack {
TextField("Username", text: $authModel.username)
.multilineTextAlignment(.center)
TextField("Password", text: $authModel.password)
.multilineTextAlignment(.center)
Button {
Task {
await authModel.login()
}
} label: {
Text("Login")
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
}

#Preview {
let authModel = AuthenticationModel(MockAPIClient())

ContentView().environmentObject(authModel)
}
< /code>
import Foundation

class AuthenticationModel: ObservableObject {
@Published var isAuthenticated = false
@Published var username = ""
@Published var password = ""

init(_ apiClient: APIClient) {
self.apiClient = apiClient
}

@MainActor
func login() async {
if !username.isEmpty && !password.isEmpty {
await apiClient.postRequest(
with: URL(string: "www.example.com/login")!,
andBody: LoginRequest(username: username, password: password))
isAuthenticated = true
}
}

@MainActor
func logout() async {
username = ""
password = ""
isAuthenticated = false
}
}
< /code>
import Foundation

protocol APIClient {
func getRequest(with url: URL) async -> T?

func postRequest(with url: URL, andBody body: T) async
}

struct RealAPIClient: APIClient {
func getRequest(with url: URL) async -> T? {
print("Real Login")
return nil
}

func postRequest(with url: URL, andBody body: T) async {
print("Real POST")
}
}

struct MockAPIClient: APIClient {
func getRequest(with url: URL) async -> T? {
print("Mock Login")
return nil
}

func postRequest(with url: URL, andBody body: T) async {
print("Mock POST")
}
}

struct LoginRequest: Encodable {
let username: String
let password: String
}
< /code>
import SwiftUI

@main
struct MockTestsApp: App {
@StateObject private var authModel = AuthenticationModel(RealAPIClient())

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(authModel)
}
}
}
UI -Test
Ein Beispiel für einen grundlegenden UI -Test, in dem sich der Benutzer anmeldet. Für diesen Test würde ich So verspotten Sie die Anmeldeanforderung, sodass der Test nicht von einem externen Authentifizierungsserver abhängt. Darüber hinaus möchte ich einen Test schreiben, bei dem der Benutzer bereits angemeldet ist, damit ich UIS testen kann, die nur für Benutzer zur Verfügung stehen, ohne für jeden dieser Tests eine Anmeldung durchzuführen. < /P>
< pre class = "Lang-Swift PrettyPrint-Override">

Code: Select all

import XCTest

final class MockTestsUITests: XCTestCase {

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.
}

override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}

@MainActor
func testLogin() throws {
let app = XCUIApplication()
app.launch()

app.textFields["Username"].tap()
app.textFields["Username"].typeText("Example")

app.textFields["Password"].tap()
app.textFields["Password"].typeText("Password")

app.buttons["Login"].tap()

XCUIApplication().staticTexts["Welcome Example"].tap()

// This test works, but I'd like to mock the login request somehow. With the current test implementation the test is dependent on a server to perform an actual login.
}

@MainActor
func testOtherFunctionality() throws {
// Mock the authentication state so a login doesn't have to be performed by this test
// Test actual other functionality
}

@MainActor
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post