Skip to content

Feature: Testing mocks for HTTPFetcher + FormParameter parsing#32

Open
ThisIsMissEm wants to merge 5 commits into
mainfrom
feat/testing-mocks
Open

Feature: Testing mocks for HTTPFetcher + FormParameter parsing#32
ThisIsMissEm wants to merge 5 commits into
mainfrom
feat/testing-mocks

Conversation

@ThisIsMissEm

@ThisIsMissEm ThisIsMissEm commented May 31, 2026

Copy link
Copy Markdown
Contributor

In order to start writing tests for oauth4swift, we needed some test helpers. This had been on my list for a while, and well, today I happened to be being annoyed by my cough so needed something to focus on.

Major changes:

  • MockHTTPFetcher: allows mocking request by URL, and optionally method
  • FormParameters+Parsing: allows decoding a .data or .queryItems() value of FormParameters, intended for use with HTTP Testing.

Minor changes, currently in GermConvenienceTesting:

  • BundledHTTPRequest is now Equatable
  • HTTPDataResponse is extended with various methods for creating successful responses and responses with a given status code (and possible data)

As MockHTTPFetcher internally uses BundledHTTPRequest for storage, so we do actually capture the request body, so we can make an assertion about the request that we send: url, headers, body, everything.

This pull request was authored with the help of Claude, however, I largely wrote the code and claude reviewed and guided me when I got stuck with swift specifics, rather than it implementing the code (I only had it implement a few minor changes)

@changeset-bot

changeset-bot Bot commented May 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a26e610

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@germ-network/germ-convenience Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ThisIsMissEm ThisIsMissEm requested a review from germ-mark May 31, 2026 02:21
New GermConvenienceTesting library target with:
  - FormParameters+Parsing: init(parsing:) extensions for Data, URLComponents, and [URLQueryItem]

This helps with evaluating whether requests we send are the correct shape.
Introduces MockHTTPFetcher which conforms to HTTPFetcher allowing easier writing of test assertions:
  - MockHTTPFetcher: method-aware HTTP mock with per-URL handler queues, exact-to-any fallback, and request logging
  - HTTPResponseError: Equatable conformance to support error matching in tests

@germ-mark germ-mark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd suggest you adopt the three target structure of "library, mocks, tests", e.g. in
https://github.com/germ-network/AtprotoTypes

Where the mocks live in a separate target, can be adopted by downstream Mock and Test Targets. The tests can then import the library and the mock.

(I have been meaning to and this would be a good time to lift the Mockable target from AtprotoTypes to GermConvenience. don't need to do it as part of this PR, but now is a good time in this next release)

let method: MethodMatcher
}

private let lock = NSLock()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what is the function of the lock here?

return self
}

public func data(for request: BundledHTTPRequest) async throws -> HTTPDataResponse {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this doesn't have any async calls in the body, so this can be a synchronous call, which makes the use of a lock seemingly spurious

public func requests(for url: URL, method: MethodMatcher) -> [BundledHTTPRequest] {
requestLog[url, default: []].filter {
switch method {
case .any: return true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can probably be an implicit return (llm's have not caught on to implicit returns)

}

private func decodeString(_ input: Substring) -> String {
return input.removingPercentEncoding ?? String(input)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

implicit return would be nice here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants