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
This commit is contained in:
Maksym Malyhin authored and GitHub committed 2021-03-09 14:32:33 -05:00
1 parent 5063d9fba5
commit c9c03467ff
8 files changed
+403

No files matched your search

@@ -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
@@ -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
View File
@@ -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
@@ -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
@@ -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
@@ -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
View File
@@ -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
View File
@@ -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