EarlGrey icon indicating copy to clipboard operation
EarlGrey copied to clipboard

EarlGrey2 CocoaPods White-box working!

Open shaneong opened this issue 5 years ago • 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)/../../<YOUR APP>.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.

shaneong avatar Feb 05 '21 16:02 shaneong

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.

badrinathvm avatar Mar 26 '21 16:03 badrinathvm

Did you launch your application in your test?

XCUIApplication().launch()

shaneong avatar Mar 28 '21 02:03 shaneong

yes launch code is present.Seeing below error after the launch of the app

*** -[NSProxy doesNotRecognizeSelector:updateFeatureFlagFor:value:] called! (NSInvalidArgumentException)

badrinathvm avatar Mar 28 '21 05:03 badrinathvm

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 avatar Mar 29 '21 19:03 tirodkar

@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

badrinathvm avatar Mar 29 '21 19:03 badrinathvm

Oh, also

//import EarlGreyTest ( This `EarlGreyTest` is not accessible here :(  )

The bundle is part of the application - this will not be accessible here.

tirodkar avatar Mar 29 '21 19:03 tirodkar

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

badrinathvm avatar Mar 29 '21 19:03 badrinathvm

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 avatar Mar 29 '21 19:03 tirodkar

So @tirodkar what u meant to say is add this in extension GREYHostApplicationDistantObject: EarlGreyUITestCommunicator { app target ? . Just tried , still seeing the same error though :(

badrinathvm avatar Mar 29 '21 20:03 badrinathvm

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

badrinathvm avatar Mar 30 '21 00:03 badrinathvm

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?

badrinathvm avatar Mar 30 '21 01:03 badrinathvm

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"

badrinathvm avatar Apr 07 '21 21:04 badrinathvm

@tirodkar started seeing below issue any other pointer on this? IMG_1063

badrinathvm avatar Apr 08 '21 20:04 badrinathvm

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.

adil-hussain-84 avatar Jan 06 '22 14:01 adil-hussain-84