Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduced TestLogHandler to simplify how we test logged messages #1858

Merged
merged 1 commit into from
Aug 31, 2022

Conversation

NachoSoto
Copy link
Contributor

@NachoSoto NachoSoto commented Aug 19, 2022

We had this fragile pattern in several tests:

var loggedMessages = [String]()
let originalLogHandler = Logger.logHandler
defer { Logger.logHandler = originalLogHandler }

Logger.logHandler = { _, message, _, _, _ in
    loggedMessages.append(message)
}

Which was too verbose and error prone.

This new type works like an RAII type. Its lifetime scopes the messages that are observed, so simply having a reference around for the lifetime of the test allows observing only those messages.

This much simpler abstraction will make it easier for us to test more of what's logged by the SDK.

Examples:

  • Initialize in setUp:
private var testLogHandler: TestLogHandler!
override func setUp() {
    super.setUp()
    self.testLogHandler = TestLogHandler()
}
override func tearDown() {
    self.testLogHandler = nil
    super.tearDown()
}

func testExample() {
    // Run code

    expect(self.testLogHandler.messages).to(contain(...))
}
  • Alternatively, TestLogHandler can be used locally within a single test:
func testExample() {
    let logHandler = TestLogHandler()

    // Run some code

    expect(logHandler.loggedMessages.onlyElement?.message) == "Expected log"
}

Notes:

This implicitly overrides Purchases.shared.verboseLogHandler while running tests, but it just wraps it by still invoking the default handler, so logs are still printed in the console.

@NachoSoto NachoSoto added test Adding missing tests or correcting existing tests refactor A code change that neither fixes a bug nor adds a feature labels Aug 19, 2022
@NachoSoto NachoSoto requested a review from a team August 19, 2022 21:45
Base automatically changed from aray-only-element to main August 22, 2022 23:00
We had this fragile pattern in several tests:
```swift
var loggedMessages = [String]()
let originalLogHandler = Logger.logHandler
defer { Logger.logHandler = originalLogHandler }

Logger.logHandler = { _, message, _, _, _ in
    loggedMessages.append(message)
}
```

Which was too verbose and error prone.

This new type works like an RAII type. Its lifetime scopes the messages that are observed, so simply having a reference around for the lifetime of the test allows observing only those messages.

For example:
```swift
private var testLogHandler: TestLogHandler!
override func setUp() {
    super.setUp()
    self.testLogHandler = TestLogHandler()
}
override func tearDown() {
    self.testLogHandler = nil
    super.tearDown()
}

func testExample() {
    // Run code

    expect(self.testLogHandler.messages).to(contain(...))
}
```

 - Alternatively, `TestLogHandler` can be used locally within a single test:

```swift
func testExample() {
    let logHandler = TestLogHandler()

    // Run some code

    expect(logHandler.loggedMessages.onlyElement?.message) == "Expected log"
}
```
@NachoSoto
Copy link
Contributor Author

@RevenueCat/core-sdk bump

Copy link
Contributor

@tonidero tonidero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of questions, but it's looking great!


private static let sharedHandler: SharedTestLogHandler = {
let handler = SharedTestLogHandler()
handler.install()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm personally, I don't like that an object contruction such as this has a side effect like this... Could we add a method on the TestLogHandler so it gets called only on the tests that require it instead? It might be more verbosity on the call side, but I think it makes it clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done lazily only when an instance is created already (Swift properties like this are lazy)

}

func install() {
Purchases.verboseLogHandler = self.logHandler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm so if I understand this correctly, when the TestLogHandler gets released, we remove the observers, but we don't reset the value of verboseLogHandler. I think the way it is right now, the behavior should be correct, but internally, we will be sharing this logHandler between tests (unless it gets reconstructed on every test). I wonder if we could reset Purchases.verboseLogHandler on the deinit as well? Not sure if there would be any issues with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would have to ensure that there aren't any observers left, which would be extra state. It's possible, but I think it's unnecessary for now, and since this only affects tests I think it's a fair compromise. This class doesn't even get compiled outside the tests.

@NachoSoto NachoSoto merged commit 58e15c8 into main Aug 31, 2022
@NachoSoto NachoSoto deleted the test-log-handler branch August 31, 2022 17:07
@aboedo
Copy link
Member

aboedo commented Sep 1, 2022

I'm late to the game but just wanted to say that I love this!!

@NachoSoto
Copy link
Contributor Author

😍

NachoSoto added a commit that referenced this pull request Sep 5, 2022
…count` setting was never explicitly set

Fixes [CSDK-454].

This should reduce a lot of the noise during initialization. The internal backing property was already `Bool?`, so we can check if it was ever set, and if not, not log anything.
Using `TestLogHandler` introduced in #1858 to verify this behavior.
NachoSoto pushed a commit that referenced this pull request Sep 8, 2022
**This is an automatic release.**

### Bugfixes
* `watchOS`: fixed crash when ran on single-target apps with Xcode 14 and before `watchOS 9.0` (#1895) via NachoSoto (@NachoSoto)
* `CustomerInfoManager`/`OfferingsManager`: improved display of underlying errors (#1888) via NachoSoto (@NachoSoto)
* `Offering`: improved confusing log for `PackageType.custom` (#1884) via NachoSoto (@NachoSoto)
* `PurchasesOrchestrator`: don't log warning if `allowSharingAppStoreAccount` setting was never explicitly set (#1885) via NachoSoto (@NachoSoto)
* Introduced type-safe `PurchasesError` and fixed some incorrect returned error types (#1879) via NachoSoto (@NachoSoto)
* `CustomerInfoManager`: fixed thread-unsafe implementation (#1878) via NachoSoto (@NachoSoto)
### New Features
* Disable SK1's `StoreKitWrapper` if SK2 is enabled and available (#1882) via NachoSoto (@NachoSoto)
* `Sendable` support (#1795) via NachoSoto (@NachoSoto)
### Other Changes
* Renamed `StoreKitWrapper` to `StoreKit1Wrapper` (#1886) via NachoSoto (@NachoSoto)
* Enabled `DEAD_CODE_STRIPPING` (#1887) via NachoSoto (@NachoSoto)
* `HTTPClient`: added `X-Client-Bundle-ID` and logged on SDK initialization (#1883) via NachoSoto (@NachoSoto)
* add link to SDK reference (#1872) via Andy Boedo (@aboedo)
* Added `StoreKit2Setting.shouldOnlyUseStoreKit2` (#1881) via NachoSoto (@NachoSoto)
* Introduced `TestLogHandler` to simplify how we test logged messages (#1858) via NachoSoto (@NachoSoto)
* `Integration Tests`: added test for purchasing `StoreProduct` instead of `Package` (#1875) via NachoSoto (@NachoSoto)
* `ErrorUtils`: added test to verify that returned errors can be converted to `ErrorCode` (#1871) via NachoSoto (@NachoSoto)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
refactor A code change that neither fixes a bug nor adds a feature test Adding missing tests or correcting existing tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants