Laravel-veneer - A suite of mocked services to use in your Laravel tests.

Overview

Laravel Veneer

A suite of fixture data and methods to help make mocking 3rd party services easier.

Overview

Laravel Veneer aims to solve two problems for developers to assist with writing tests:

  1. Provide static response data (fixtures) from 3rd party APIs
  2. Provide SDK-specific abstractions on top of Mockery for mocking SDK calls

Both goals aim to help with 80% of the work needed for mocking/API responses, while leaving the remaining 20% up to the developer. As an example, Laravel Veneer will provide a generic mock and fixture for creating a new calendar via Cronofy, but if you need to test for a specific response, you'll need to mock that yourself.

Fixtures

Fixtures are static response data from 3rd party APIs such as Twitter, GitHub, Cronofy, etc. One goal of this package is to allow the community to contribute response fixtures for any available API.

SDK Mocking

The SDK mocking layer is built specifically for Laravel, on top of Mockery. It allows developers to quickly mock various SDK calls for available 3rd party APIs, when using their SDK packages.

Installation

Install the package via composer:

composer require ohseesoftware/laravel-veneer

There's no service provider or anything registered, all classes are used directly in your test classes.

Usage

The goal of the package is to make mocking the 80% use case easy. There's three steps involved:

  1. Create an instance of a MockProvider
  2. Add the methods you want to mock to the MockProvider
  3. Apply the mock

The simplest example is a one-liner:

$this->veneer(CronofyMock::make()->add(CreateChannelMock::make()));

Where we:

  • Make a new instance of the MockProvider: CronofyMock::make()
  • Add a method to mock: ->add(CreateChannelMock::make())
  • Apply the mock using the exposed trait: $this->veneer(...)

Here's an example of a full test class using the package to a mock Cronofy's createChannel method:

<?php

use OhSeeSoftware\LaravelVeneer\Providers\Cronofy\CreateChannelMock;
use OhSeeSoftware\LaravelVeneer\Providers\Cronofy\CronofyMock;
use OhSeeSoftware\LaravelVeneer\Traits\VeneerMocks;

class MyTest extends TestCase
{
    use VeneerMocks;
    
    public function testItDoesSomething()
    {
        // Given, mock the 3rd party SDK
        $this->veneer(CronofyMock::make()->add(CreateChannelMock::make()));

        // When, call your code that will call the 3rd party SDK
        $this->post('/channel');

        // Then, assert your code handled the 3rd party response correctly
        $this->assertDatabaseHas('channels', [
            'cronofy_channel_id' => 'chn_0000000000'
        ]);
    }
}

If you're wondering where the hard coded chn_0000000000 comes from, it comes from the fixture data that has been defined.

Using the trait

The VeneerMocks trait defines a single method which sets the current $application instance on the given provider, and then calls the mock method. The mock method will apply the mocked methods as a partial mock via Laravel's $this->partialMock method.

Overriding mock responses

If you're not satisfied with the default fixture response, you can override it yourself:

$this->veneer(
    CronofyMock::make()->add(
        CreateChannelMock::make()->return('Hello world!')
    )
);

Now, when the createChannel method is called, it will return 'Hello world!' instead of the default fixture data.

Merging mock responses with your data

Okay, well now let's say you only want to tweak one part of the fixture data, say the channel ID that is returned. You can do so via the merge($key, $value) method:

$this->veneer(
    CronofyMock::make()->add(
        CreateChannelMock::make()->merge('channel.channel_id', 'chn_123456')
    )
);

Now, when the createChannel method is called, it will return the default fixture data, but the channel.channel_id value will be set to chn_123456, which means your test would now look like:

public function testItDoesSomething()
{
    // Given, mock the 3rd party SDK with a custom `channel.channel_id` value
    $this->veneer(
        CronofyMock::make()->add(
            CreateChannelMock::make()->merge('channel.channel_id', 'chn_123456')
        )
    );

    // When, call your code that will call the 3rd party SDK
    $this->post('/channel');

    // Then, assert your code handled the 3rd party response correctly
    $this->assertDatabaseHas('channels', [
        'cronofy_channel_id' => 'chn_123456'
    ]);
}

Expecting arguments

You can utilize Mockery's withArgs method via with(...$args) method:

$this->veneer(
    CronofyMock::make()->add(
        CreateChannelMock::make()->with('test')
    )
);

When the mocked method is called, it will verify that the value test was passed into the method. If test is not passed, the test will fail.

Calling the mocked method multiple times

By default, Laravel Veneer expects that all mocked methods will be called once. However, if you need to have the method mocked for multiple calls, you can use the times(int $times) method:

$this->veneer(
    CronofyMock::make()->add(
        CreateChannelMock::make()->times(3)
    )
);

In the above example, if the mocked method is not called exactly 3 times, the test will fail.

Contributing

The following sections will outline the guidelines for contributing new fixtures and SDK mocks.

Contributing Fixtures

When adding fixtures, please try to adhere to the following guidelines. You can view the existing fixtures to see how they are structured.

Format

Currently, Laravel Veneer expects all fixtures to be defined in JSON. This may change in the future, but for the initial work, we want to focus on JSON endpoints.

Folder structure

The folder structure for fixtures is a little different depending on if the fixture is for a HTTP response or an incoming webhook payload.

For a HTTP response, the guideline is:

/fixtures/{name_of_service}/responses/{version?}/{path}/{method}.json

Where:

  • name_of_service is the name of the service the fixture belongs to
  • version is only required for responses, and should be set to the API's version, if applicable
  • path is the path of the endpoint
  • method is the HTTP method used to call the endpoint

As an example, here's the path for creating a new tweet using Twitter's v2 API:

/fixtures/twitter/responses/v2/tweets/post.json

For webhook payload fixtures, the guideline is a bit simpler:

/fixtures/{name_of_service}/webhooks/{event}.json

Where:

  • name_of_service is the name of the service the fixture belongs to
  • event is the name of the event that triggered the webhook

As an example, here's the path for Cronofy's changeNotification webhook payload:

/fixtures/cronofy/webhooks/changeNotification.json

Contributing SDK Mocks

For adding new SDK mocks, there's two pieces involved:

  1. Add a new class to define which class you are mocking, we call this a MockProvider
  2. Add a new class for the method you are mocking, we call this a MockedMethod

Adding a new MockProvider

The MockProvider usage is quite simple. All you need to do is extend the MockProvider class and then implement a method telling Laravel Veneer which class you are mocking:

<?php

namespace OhSeeSoftware\LaravelVeneer\Providers\Cronofy;

use OhSeeSoftware\LaravelVeneer\Providers\MockProvider;

class CronofyMock extends MockProvider
{
    /**
     * FQDN of the class being mocked.
     */
    public function mockedClass(): string
    {
        // Laravel Veneer will apply a partial mock to the `\Cronofy\Cronofy` class
        return \Cronofy\Cronofy::class;
    }
}

Adding a new MockedMethod

Adding a new MockedMethod is also quite simple, but allows for more configuration. You'll need to create a new class that extends the MockedMethod class, and implement the required abstract methods:

<?php

namespace OhSeeSoftware\LaravelVeneer\Providers\Cronofy\Channels;

use OhSeeSoftware\LaravelVeneer\Providers\MockedMethod;

class ListChannelsMock extends MockedMethod
{
    public function method(): string
    {
        /**
         * Name of the method being mocked.
         */
        return 'listChannels';
    }
}

By default, the only required method is method(): string, which tells Laravel Veneer which method of the MockProvider class you are mocking.

If you want your mocked method to return data from a fixture, define the path to the fixture via fixturePath(): ?string:

/**
 * Path to the fixture data file, if applicable.
 *
 * The value should be a relative path from the `fixtures` directory.
 */
public function fixturePath(): ?string
{
    return 'cronofy/responses/v1/channels/index.json';
}

If you need your mocked method to return something entirely custom (maybe a new instance of a different class, etc), you can override the result() method:

/**
 * The result returned from the mocked method.
 */
public function result()
{
    return 'hello world!'; // Return 'hello world!' whenever this mocked method is called
}
You might also like...
Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

Introduction Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services. It handles almost all of the boilerpl

Laravel Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services.

Introduction Laravel Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services. It handles almost all of the b

Generate services in Laravel with the artisan command
Generate services in Laravel with the artisan command

Laravel Service Generator Quickly generate services for your projects! Table of Contents Features Installation Usage Generate services Generate servic

Collection of Google Maps API Web Services for Laravel

Collection of Google Maps API Web Services for Laravel Provides convenient way of setting up and making requests to Maps API from Laravel application.

Generate services and contracts in Laravel with the artisan command
Generate services and contracts in Laravel with the artisan command

What does it do? This package adds a new php artisan make:service {name} {--N|noContract} command. It will create a service file and its contract (int

Easily generate URLs to Minecraft avatars with the ability to switch between services

Minecraft Avatar URLs This library provides PHP utilities to generate URLs to Minecraft Avatars in different formats with the ability to easily change

A simple to use query builder for the jQuery QueryBuilder plugin for use with Laravel.
A simple to use query builder for the jQuery QueryBuilder plugin for use with Laravel.

QueryBuilderParser Status Label Status Value Build Insights Code Climate Test Coverage QueryBuilderParser is designed mainly to be used inside Laravel

A Laravel package that allows you to use multiple
A Laravel package that allows you to use multiple ".env" files in a precedent manner. Use ".env" files per domain (multi-tentant)!

Laravel Multi ENVs Use multiple .envs files and have a chain of precedence for the environment variables in these different .envs files. Use the .env

A super simple package allowing for use MySQL 'USE INDEX' and 'FORCE INDEX' statements.
A super simple package allowing for use MySQL 'USE INDEX' and 'FORCE INDEX' statements.

Laravel MySQL Use Index Scope A super simple package allowing for use MySQL USE INDEX and FORCE INDEX statements. Requirements PHP ^7.4 | ^8.0 Laravel

Releases(v1.4.0)
Owner
Oh See Software
Software division of Oh See Media.
Oh See Software
Zarinpal is a laravel package to easily use zarinpal.com payment services in your applications

پکیج اتصال به درگاه پرداخت زرین پال zarinpal.com برای اتصال به درگاه پرداخت اینترنتی زرین پال و استفاده از api های آن می توانید از این پکیج استفاده کن

Rahmat Waisi 4 Jan 26, 2022
Perform Self-Diagnosis Tests On Your Laravel Application

Perform Self-Diagnosis Tests On Your Laravel Application This package allows you to run self-diagnosis tests on your Laravel application. It comes wit

Beyond Code 1.4k Dec 13, 2022
Chrome extension to generate Laravel integration tests while using your app.

Laravel TestTools Check out the introduction post about the chrome extension. Installation git clone [email protected]:mpociot/laravel-testtools.git # i

Marcel Pociot 473 Nov 1, 2022
Testbench Component is the de-facto package that has been designed to help you write tests for your Laravel package

Laravel Testing Helper for Packages Development Testbench Component is the de-facto package that has been designed to help you write tests for your La

Orchestra Platform 1.9k Dec 29, 2022
This package provides a trait to run your tests against a MinIO S3 server.

Laravel MinIO Testing Tools This package provides a trait to run your tests against a MinIO S3 server. ?? Blog post: https://protone.media/en/blog/how

Protone Media 7 Oct 12, 2022
Fully customizable and tests supported Laravel admin dashboard for developers.

Laravel Admin dashboard Like Laravel Jetstream but built with Hotwire Turbo + additional perks. Tools used: tailwindcomponents/dashboard Hotwire Turbo

null 12 Nov 1, 2022
Removes whitelisted unnecessary files (like tests/docs etc.) from vendor directory

Composer vendor cleanup This is a simple script for the Composer to remove unnecessary files (documentation/examples/tests etc.) from included vendor

Chris 2 Nov 14, 2022
Generator-hedley - Scaffold a headless Drupal backend, Angular app client, and Behat tests

generator-hedley Scaffold a headless Drupal backend, Angular app client, and Behat tests Hedley is a yeoman generator that scaffolds a headless Drupal

null 99 Jun 3, 2022
A package for integrating Lazerpay services with your laravel application

This is my package lazerpay-laravel This is where your description should go. Limit it to a paragraph or two. Consider adding a small example. Support

Abdulsalam Ishaq 5 Dec 22, 2022
A Laravel (php) package to interface with the geo-location services at geonames.org.

geonames v7.x A Laravel (php) package to interface with the geo-location services at geonames.org. Major Version Jump I jumped several major versions

Michael Drennen 82 Nov 24, 2022