Skip to content

Commit

Permalink
Merge pull request #2 from DeBoerTool/feature
Browse files Browse the repository at this point in the history
new: docs
  • Loading branch information
danielsdeboer authored Mar 27, 2023
2 parents 02697b1 + 346a38f commit aa99087
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 25 deletions.
75 changes: 72 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,85 @@
# PHP Project Template
# Client Fake for Laravel

This package provides an easy way to fake HTTP calls for services that requires Laravel's HTTP Client in testing contexts. If you're using the HTTP Facade, this package isn't for you.

Simply extend or directly construct the `ClientFake`, register your fakes, then commit or invoke the result. When your service resolves the HTTP Client from the container, it will be resolved with the fakes applied only to that instance of the HTTP Client.

For instance if you have `FooService` and `BarService`, if you register a fake for `BarService`, the fake only be applied to that service, and not to any others.

This is useful when you have one service that you want to fake, but not others.

## Installation

You can install the package via composer:

```bash
composer require dbt/php-project
composer require dbt/client-fake
```

## Usage

Change the project name and namespaces in `composer.json` and also in `UnitTestCase` and away you go.
You can use the `ClientFake` class directly, or extend it and define method fakes of your own.

If you want to use the `ClientFake` directly, you can do so like this (for example, in the body of your test):

```php
use Dbt\ClientFake\ClientFake;
use Dbt\ClientFake\ClientFakeOptions;

$clientFake = new ClientFake($app, new ClientFakeOptions(
service: MyService::class,
base: 'https://my-service.com',
// Or false if the API isn't versioned.
version: 'v1',
// Optional headers to add to all fake responses for use in testing.
headers: ['X-My-Header' => 'some value'],
));

$clientFake->fake('my/endpoint', ['data' => 'some data']);

$clientFake->commit();
// or
$clientFake();
```

Then you can resolve your service and use it as normal:

```php
$service = resolve(MyService::class);

$response = $service->callMyEndpoint();

$this->assertSame(['data' => 'some data'], $response->json());
$this->assertSame('some value', $response->header('X-My-Header'));
```

### Catchall

By default, a catchall is added, which will return a 500 response for any endpoint that isn't faked. You can disable this:

```php
$clientFake->withoutCatchall();
```

### Conditionally Enabling

You can conditionally enable and disable the fake by using the `enable` method:

```php
$clientFake->enable($booleanCondition);
```

You can optionally add a callback that will be resolved from the container and executed when the boolean condition is false:

```php
$clientFake->enable($booleanCondition, function (SomeOtherService $service) {
$service->doSomething();
});
```

### Options

You can use the `ClientFakeOptions` object as-is, or define your own by implementing the `ClientFakeOptionsInterface`.

## Etc.

Expand Down
8 changes: 4 additions & 4 deletions Source/ClientFake.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class ClientFake

public function __construct(
protected readonly Application $app,
protected readonly ClientFakeOptions $options,
protected readonly ClientFakeOptionsInterface $options,
Generator|null $faker = null,
) {
$this->faker = $faker ?? Factory::create();
Expand Down Expand Up @@ -88,7 +88,7 @@ public function commit(): self
$this->fakes['*'] = fn (Request $request) => Http::response(
sprintf(
'%s catchall caught this url: %s',
$this->options->service,
$this->options->service(),
$request->url(),
),
500,
Expand All @@ -99,7 +99,7 @@ public function commit(): self
// container. However, we're intending to target a specific service
// here, so we use a focused binding. This means that other services
// using the HTTP Client will not be affected.
$this->app->when($this->options->service)
$this->app->when($this->options->service())
->needs(HttpClientFactory::class)
->give(fn () => (new HttpClientFactory())
->fake($this->fakes));
Expand Down Expand Up @@ -127,7 +127,7 @@ public function fake(
$this->fakes[$this->url($url)] = HttpClientFactory::response(
is_array($data) ? $data : $this->app->call($data),
$code,
$this->options->headers,
$this->options->headers(),
);

return $this;
Expand Down
27 changes: 17 additions & 10 deletions Source/ClientFakeOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
namespace Dbt\ClientFake;

/**
* A class that holds the options for the ClientFakeAbstract. How you populate
* this is up to you. Usually these values come from your services config file.
* A class that defines the options for the ClientFake.
*/
class ClientFakeOptions
class ClientFakeOptions implements ClientFakeOptionsInterface
{
/**
* @param string $service The fully-qualified class name of the service.
Expand All @@ -22,13 +21,21 @@ public function __construct(
) {
}

/**
* @param string|array $fragment The URL fragment to append to the base URL.
* If an array is passed in, the first element
* will be used as the format string, and the
* remaining elements will be used as the
* arguments.
*/
public function service(): string
{
return $this->service;
}

public function base(): string
{
return $this->base;
}

public function headers(): array
{
return $this->headers;
}

public function url(string|array $fragment): string
{
$url = is_array($fragment)
Expand Down
28 changes: 28 additions & 0 deletions Source/ClientFakeOptionsInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Dbt\ClientFake;

/**
* An interface that represents the required option for the ClientFake. You can
* simply use the provided ClientFakeOptions object, but if you wish to define
* your own, it must implement this interface.
*/
interface ClientFakeOptionsInterface
{
/**
* @param string|array $fragment The URL fragment to append to the base URL.
* If an array is passed in, the first element will be used as the format
* string, and the remaining elements will be used as the arguments.
*/
public function url(string|array $fragment): string;

/**
* Get the fully-qualified class name of the service.
*/
public function service(): string;

/**
* Get the array of headers to be added to fake requests.
*/
public function headers(): array;
}
12 changes: 4 additions & 8 deletions Tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,17 @@ protected function assertFakeResponse(Response $response): void
{
$this->assertSame(
'true',
$this->getFakeHeader($response),
$response->header('Fake'),
'No "Fake" header was found on the Response.',
);
}

protected function assertRealResponse(Response $response): void
{
$this->assertNull(
$this->getFakeHeader($response),
$this->assertSame(
'',
$response->header('Fake'),
'A "Fake" header was found on the Response.',
);
}

private function getFakeHeader(Response $response): string|null
{
return $response->headers()['Fake'][0] ?? null;
}
}

0 comments on commit aa99087

Please sign in to comment.