Code: Select all
let app = XCUIApplication()
app.launch()
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
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)
}
}
}
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()
}
}
}
}