Przeglądaj źródła

Prototype of a module with Firestore fakes for testing dependent code in Swift (#7668)

* Firestore testing support prototype

* SwiftPM target

* Comments and formatting

* Move FirebaseFirestoreTestingSupport.podspec to subfolder

* Swift PM updated

* Headers location and imports

* Podspec version update

* Brief readme

* Swift PM FirestoreTestingSupportTests target added

* Formatting

* Move FirebaseFirestoreTestingSupport.podspec back to root

* README updates

* Cleanup podspec

* README
Maksym Malyhin 5 lat temu
rodzic
commit
c9c03467ff

+ 108 - 0
.swiftpm/xcode/xcshareddata/xcschemes/Firebase-Package.xcscheme

@@ -734,6 +734,104 @@
                ReferencedContainer = "container:">
             </BuildableReference>
          </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseAuthCombineSwift-Beta"
+               BuildableName = "FirebaseAuthCombineSwift-Beta"
+               BlueprintName = "FirebaseAuthCombineSwift-Beta"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseCombineSwift-Beta"
+               BuildableName = "FirebaseCombineSwift-Beta"
+               BlueprintName = "FirebaseCombineSwift-Beta"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseFunctionsCombineSwift-Beta"
+               BuildableName = "FirebaseFunctionsCombineSwift-Beta"
+               BlueprintName = "FirebaseFunctionsCombineSwift-Beta"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseAuthCombineSwift"
+               BuildableName = "FirebaseAuthCombineSwift"
+               BlueprintName = "FirebaseAuthCombineSwift"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseCombineSwift"
+               BuildableName = "FirebaseCombineSwift"
+               BlueprintName = "FirebaseCombineSwift"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseFirestoreTestingSupport"
+               BuildableName = "FirebaseFirestoreTestingSupport"
+               BlueprintName = "FirebaseFirestoreTestingSupport"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseFunctionsCombineSwift"
+               BuildableName = "FirebaseFunctionsCombineSwift"
+               BlueprintName = "FirebaseFunctionsCombineSwift"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
       </BuildActionEntries>
    </BuildAction>
    <TestAction
@@ -922,6 +1020,16 @@
                ReferencedContainer = "container:">
             </BuildableReference>
          </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirestoreTestingSupportTests"
+               BuildableName = "FirestoreTestingSupportTests"
+               BlueprintName = "FirestoreTestingSupportTests"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </TestableReference>
       </Testables>
    </TestAction>
    <LaunchAction

+ 52 - 0
.swiftpm/xcode/xcshareddata/xcschemes/FirestoreTestingSupportTests.xcscheme

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1240"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirestoreTestingSupportTests"
+               BuildableName = "FirestoreTestingSupportTests"
+               BlueprintName = "FirestoreTestingSupportTests"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 58 - 0
FirebaseFirestoreTestingSupport.podspec

@@ -0,0 +1,58 @@
+Pod::Spec.new do |s|
+  s.name                    = 'FirebaseFirestoreTestingSupport'
+  s.version                 = '1.0.0'
+  s.summary                 = 'Firebase SDKs testing support types and utilities.'
+
+  s.description      = <<-DESC
+  Type declarations and utilities needed for unit testing of the code dependent on Firebase SDKs
+                       DESC
+
+  s.homepage                = 'https://developers.google.com/'
+  s.license                 = { :type => 'Apache', :file => 'LICENSE' }
+  s.authors                 = 'Google, Inc.'
+
+  s.source                  = {
+    :git => 'https://github.com/Firebase/firebase-ios-sdk.git',
+    :tag => 'CocoaPods-' + s.version.to_s
+  }
+
+  ios_deployment_target = '10.0'
+  osx_deployment_target = '10.12'
+  tvos_deployment_target = '10.0'
+  watchos_deployment_target = '6.0'
+
+  s.ios.deployment_target = ios_deployment_target
+  s.osx.deployment_target = osx_deployment_target
+  s.tvos.deployment_target = tvos_deployment_target
+  s.watchos.deployment_target = watchos_deployment_target
+
+  s.cocoapods_version       = '>= 1.4.0'
+  s.prefix_header_file      = false
+  s.requires_arc            = true
+
+  base_dir = 'FirebaseTestingSupport/Firestore/'
+
+  s.source_files = [
+    base_dir + 'Sources/**/*.{m,mm,h}',
+    'Firestore/Source/API/*\+Internal.h'
+  ]
+
+  s.public_header_files = base_dir + '**/*.h'
+
+  s.dependency 'FirebaseFirestore', '~> 7.7'
+
+  s.pod_target_xcconfig = {
+    'GCC_C_LANGUAGE_STANDARD' => 'c99',
+    'OTHER_CFLAGS' => '-fno-autolink',
+    'HEADER_SEARCH_PATHS' =>
+      '"${PODS_TARGET_SRCROOT}" '
+  }
+
+  s.test_spec 'unit' do |unit_tests|
+    unit_tests.scheme = { :code_coverage => true }
+    unit_tests.platforms = {:ios => ios_deployment_target, :osx => osx_deployment_target, :tvos => tvos_deployment_target}
+    unit_tests.source_files = [
+      base_dir + 'Tests/**/*.swift'
+    ]
+  end
+end

+ 32 - 0
FirebaseTestingSupport/Firestore/Sources/FIRQueryFake.mm

@@ -0,0 +1,32 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FirebaseTestingSupport/Firestore/Sources/Public/FirebaseFirestoreTestingSupport/FIRQueryFake.h"
+
+#import "Firestore/Source/API/FIRQuery+Internal.h"
+
+@implementation FIRQueryFake
+
+- (instancetype)init {
+  // The object is partially initialized. Make sure the methods used during testing are overridden.
+  return self;
+}
+
+- (void)getDocumentsWithCompletion:(FIRQuerySnapshotBlock)completion {
+  if (self.getDocumentsHandler) {
+    self.getDocumentsHandler(completion);
+  }
+}
+
+@end

+ 34 - 0
FirebaseTestingSupport/Firestore/Sources/Public/FirebaseFirestoreTestingSupport/FIRQueryFake.h

@@ -0,0 +1,34 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <FirebaseFirestore/FIRQuery.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void (^FIRFirestoreGetDocumentsHandler)(FIRQuerySnapshotBlock completion);
+
+/// A fake object to replace a real `Query` in tests.
+NS_SWIFT_NAME(QueryFake)
+@interface FIRQueryFake : FIRQuery
+
+- (instancetype)init;
+
+/// The block to be called each time when `getDocuments(completion:)` method is called.
+@property(nonatomic, nullable, copy) FIRFirestoreGetDocumentsHandler getDocumentsHandler;
+
+// TODO: Implement other handlers as needed.
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 44 - 0
FirebaseTestingSupport/Firestore/Tests/QueryFakeTests.swift

@@ -0,0 +1,44 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import XCTest
+@testable import FirebaseFirestoreTestingSupport
+
+class QueryFakeTests: XCTestCase {
+  func testQueryFakeConstructor() throws {
+    let fakeQuery = QueryFake()
+    XCTAssertNotNil(fakeQuery)
+    XCTAssertTrue(fakeQuery.isKind(of: Query.self))
+  }
+
+  func testQueryFakeGetDocumentsHandler() {
+    let fakeQuery = QueryFake()
+
+    let handlerExpectation = expectation(description: "Handler called")
+    fakeQuery.getDocumentsHandler = { completion in
+      handlerExpectation.fulfill()
+
+      completion(nil, nil)
+    }
+
+    let completionExpectation = expectation(description: "Completion called")
+    fakeQuery.getDocuments { snapshot, error in
+      completionExpectation.fulfill()
+      XCTAssertNil(snapshot)
+      XCTAssertNil(error)
+    }
+
+    wait(for: [handlerExpectation, completionExpectation], timeout: 0.5)
+  }
+}

+ 53 - 0
FirebaseTestingSupport/README.md

@@ -0,0 +1,53 @@
+# Firebase Testing Support (for automated test developers)
+
+Firebase Testing Support is a collection of libraries that provide type definitions and tools required for writing tests for code that uses Firebase, e.g.:
+
+- Instances of types like `Query` cannot be created with a simple constructor which makes unit testing of the code that depends on them difficult/impossible. Firestore Testing Support lib provides a type `QueryFake` that can be instantiated and used instead of actual `Query` instances in the tests.
+
+## Usage
+
+### Add dependency to your test target
+
+#### Cocoapods
+
+```
+tests.dependency 'FirebaseFirestoreTestingSupport', '~> 1.0'
+```
+
+#### Swift Package Manager
+
+```
+dependencies: [
+    ...
+    "FirebaseFirestoreTestingSupport"
+],
+
+```
+
+### Use the fake types in the tests instead of real types
+
+See test for example, e.g. [QueryFakeTests.swift](../FirebaseTestingSupport/Firestore/Tests/QueryFakeTests.swift).
+
+## Development
+
+### Generate project
+
+#### Cocoapods
+
+E.g. for `FirebaseFirestoreTestingSupport` run the following command for the root repo directory:
+
+```
+pod gen --auto-open --local-sources=./ FirebaseFirestoreTestingSupport.podspec --platforms=ios
+```
+
+#### Swift Package Manager
+
+- Open the main package definition:
+
+```
+xed Package.swift
+```
+
+- Select (or add if not exists yet) a scheme for the test target, e.g. `FirestoreTestingSupportTests`
+
+- make required modifications and run tests as needed.

+ 22 - 0
Package.swift

@@ -837,6 +837,28 @@ let package = Package(
         .define("FIR_VERSION", to: firebaseVersion),
       ]
     ),
+
+    // MARK: Testing support
+
+    .target(
+      name: "FirebaseFirestoreTestingSupport",
+      dependencies: ["FirebaseFirestore"],
+      path: "FirebaseTestingSupport/Firestore/Sources",
+      publicHeadersPath: "./",
+      cSettings: [
+        .headerSearchPath("../../.."),
+        .headerSearchPath("../../../Firestore/Source/Public/FirebaseFirestore"),
+      ]
+    ),
+    .testTarget(
+      name: "FirestoreTestingSupportTests",
+      dependencies: ["FirebaseFirestoreTestingSupport"],
+      path: "FirebaseTestingSupport/Firestore/Tests",
+      cSettings: [
+        .headerSearchPath("../../.."),
+      ]
+    ),
+
   ],
   cLanguageStandard: .c99,
   cxxLanguageStandard: CXXLanguageStandard.gnucxx14