google/EarlGrey

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.

  1. Create the generic eDistantObject bundle and UI test bundle as specified in the Xcodeproj white-box setup.
  2. 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
 

  1. Run pod install, this will add the EarlGreyApp and EarlGreyTest pods to your project
  2. Add a Copy Files phase in UI test bundle to copy AppFramework.framework to

$(TARGET_BUILD_DIR)/../../.app/Frameworks

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

Screen Shot 2021-03-26 at 9 26 03 AM

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.
Screen Shot 2021-03-29 at 5 52 08 PM

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?

Screen Shot 2021-03-29 at 6 52 47 PM

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
Screen Shot 2021-03-29 at 10 15 45 PM

for Helper Bundle i have

Screen Shot 2021-03-29 at 10 15 13 PM

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?
IMG_1063

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.