EarlGrey2 CocoaPods White-box working!
shaneong opened this issue · 14 comments
Hi all,
Many thanks to @tirodkar for his help in this.
EarlGrey 2 CocoaPods with white-box testing working example:
We recently migrated from EarlGrey 1 to EarlGrey 2, since we installed EarlGrey 1 with CocoaPods for white-box testing, it was important to us to enable EarlGrey 2 with similar setup and functionality, while unlocking iOS 14 and other features like deeplinking and crash reporting.
Here are the key gotchas that we encountered which will hopefully help another team enable EarlGrey 2 with whitebox testing installed using CocoaPods.
- Create the generic eDistantObject bundle and UI test bundle as specified in the Xcodeproj white-box setup.
- In the Podfile, add EarlGreyApp to the eDistantObject bundle instead of the application target, then weaklink EarlGreyApp to the eDistantObject bundle. Add EarlGrey Test to the UI test bundle.
P.S. This works with !use_frameworks, as you can selectively add !use_frameworks to each target that needs it and omit it where necessary.
To weaklink EarlGreyApp to eDistantObject bundle, add it as you would, then add the following to Podfile:
post_install do |installer|
# AppFramework is installed by EarlGreyApp and must be weakly linked to lazy load
# symbols as eDistantObjectBundle is injected into the app
targets_to_weaklink=['eDistantObjectBundle']
frameworks_to_weaklink=['AppFramework']
targets_to_weaklink.map!{|t| t="Pods-#{t}"}
installer.pods_project.targets.each do |target|
next unless targets_to_weaklink.include?(target.name)
target.build_configurations.each do |config|
base_config_reference = config.base_configuration_reference
unless base_config_reference.nil?
xcconfig_path = base_config_reference.real_path
xcconfig = File.read(xcconfig_path)
frameworks_to_weaklink.each do |framework|
xcconfig = xcconfig.gsub(/-framework "#{framework}"/, "-weak_framework \"#{framework}\"")
end
File.open(xcconfig_path, "w") { |file| file << xcconfig }
end
end
end
end
- Run pod install, this will add the EarlGreyApp and EarlGreyTest pods to your project
- Add a Copy Files phase in UI test bundle to copy AppFramework.framework to
$(TARGET_BUILD_DIR)/../../.app/Frameworks
- Recommend to use a "generic UI test bundle" to run this step once, so that if more UI test bundles are duplicated/added, this step isn't rerun multiple times.
That's it, I think. You can now continue to set up rest of white-box setup as specified in the current white-box setup doc.
This will enable EarlGrey 2, but also avoids other issues where the app will not open if not run by EarlGrey 2.
Hi @tirodkar, Followed the above approach along with white box testing ( https://github.com/google/EarlGrey/blob/earlgrey2/docs/swift-white-boxing.md ). We are able to execute UI tests. However, with whit box testing, it fails for the below error
We have some thing like below in our HeelperBundle
We wrote an EarlGreyCommunicator
in Helper Bundle to access variable/function state in UI tests. EarlGreyCommunicator
is a protocol to update the feature flag when the UI test gets executed.
@objc
protocol EarlGreyUITestCommunicator {
func updateFeatureFlag(for flagName: String, value: Bool)
}
However not able to access EarlGreyTest we need that because we are writing an extension on
//import EarlGreyTest ( This `EarlGreyTest` is not accessible here :( )
extension GREYHostApplicationDistantObject: EarlGreyUITestCommunicator {
func updateFeatureFlag(for flagName: String, value: Bool) {
//code for updating the feature flag.
}
}
There is one small thing we might be missing. Any more pointer in solving this issue would be greatly appreciated.
Did you launch your application in your test?
XCUIApplication().launch()
yes launch code is present.Seeing below error after the launch of the app
*** -[NSProxy doesNotRecognizeSelector:updateFeatureFlagFor:value:] called! (NSInvalidArgumentException)
The error seems pretty straightforward actually - as Shane pointed out here, you must add a post-install script in your CocoaPods to get the host bundles to work. It seems like your bundle isn't actually linked into the application process which is causing this problem.
@tirodkar i have done the post_install steps as well in the Podfile. Still the same issue.
Any thing else missing?
post_install do |installer|
targets_to_weaklink=['XXX-EarlGreyUITestHelperBundle']
frameworks_to_weaklink=['AppFramework']
targets_to_weaklink.map!{|t| t="Pods-#{t}"}
installer.pods_project.targets.each do |target|
next unless targets_to_weaklink.include?(target.name)
target.build_configurations.each do |config|
base_config_reference = config.base_configuration_reference
unless base_config_reference.nil?
xcconfig_path = base_config_reference.real_path
xcconfig = File.read(xcconfig_path)
frameworks_to_weaklink.each do |framework|
xcconfig = xcconfig.gsub(/-framework "#{framework}"/, "-weak_framework \"#{framework}\"")
end
File.open(xcconfig_path, "w") { |file| file << xcconfig }
end
end
end
end
Oh, also
//import EarlGreyTest ( This `EarlGreyTest` is not accessible here :( )
The bundle is part of the application - this will not be accessible here.
@tirodkar i tried moving this extension GREYHostApplicationDistantObject: EarlGreyUITestCommunicator {
to UI test target as EarlGreyTest
isnt accessible in Helper Bundle , it compiles fine though but when it tries to access the host. updateFeatureFlag
objects methods i see *** -[NSProxy doesNotRecognizeSelector:updateFeatureFlagFor:value:] called! (NSInvalidArgumentException)
error.
Sorry if this isn't clear in the white-boxing docs but you cannot have the whole GREYHostApplicationDistantObject added in the UI Test target as it is a purely app side object. The protocol which defines the function declarations must be added to both the app and the test target. Please take a look at the SwiftTests in the FunctionalProject for more information.
So @tirodkar what u meant to say is add this in extension GREYHostApplicationDistantObject: EarlGreyUITestCommunicator {
app target ? . Just tried , still seeing the same error though :(
Sorry if this isn't clear in the white-boxing docs but you cannot have the whole GREYHostApplicationDistantObject added in the UI Test target as it is a purely app side object. The protocol which defines the function declarations must be added to both the app and the test target. Please take a look at the SwiftTests in the FunctionalProject for more information.
@tirodkar we are performing weak linking.
Hi @shaneong what does your Helper bundle bridging header file contain?
Also does ur PODS_ROOT/EarlGreyApp/
contains the below files or more than that?
Also, what's the value of the USER HEADER SEARCH PATH of Helper bundle & UI Test Target in Build Settings?
I have below one for UI test Target
for Helper Bundle i have
Can u please share what things u have on ur project?
Got it working by adding below two entries in the Build settings HEADER_SEARCH_PATHS
for the Helper Bundle
"${PODS_CONFIGURATION_BUILD_DIR}/eDistantObject/eDistantObject.framework/Headers"
"${PODS_CONFIGURATION_BUILD_DIR}/EarlGreyTest/EarlGreyTest.framework/Headers"
@tirodkar started seeing below issue any other pointer on this?
Thank you @shaneong for sharing the extra steps that we need to follow to get white-boxing working with CocoaPods 👍
I had to alter the post_install
script that you shared to weak link a few more frameworks and not just 'AppFramework'. I have put together a minimal Xcode workspace here which demonstrates this. See the Podfile in that workspace in particular. Note that I added the 'EarlGreyTest' pod to my helper bundle as well as 'EarlGreyApp' so that I could import the 'EarlGreyTest' module in the classes defined in my helper bundle.
I will endeavour to call out other subtleties in the README file in that workspace so that other developers can have a smoother ride in getting white-boxing working with CocoaPods.