PHPackages                             getdkan/mock-chain - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. getdkan/mock-chain

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

getdkan/mock-chain
==================

A library that helps create chains of mocked objects.

1.4.2(4mo ago)2164.7k↑167.8%[1 PRs](https://github.com/GetDKAN/mock-chain/pulls)4GPL-3.0-onlyPHPPHP &gt;=7.4 &lt;9.0

Since Feb 27Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/GetDKAN/mock-chain)[ Packagist](https://packagist.org/packages/getdkan/mock-chain)[ RSS](/packages/getdkan-mock-chain/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (4)Versions (24)Used By (4)

mock-chain
==========

[](#mock-chain)

[![CircleCI](https://camo.githubusercontent.com/4933da7b0ebb4fd10503a94cbb09a86b7795aa4e11a8cb3bbbbd78cbd482c942/68747470733a2f2f636972636c6563692e636f6d2f67682f476574444b414e2f6d6f636b2d636861696e2e7376673f7374796c653d737667)](https://circleci.com/gh/GetDKAN/mock-chain)[![Maintainability](https://camo.githubusercontent.com/40ecbfb6b0b4e879ca04dad6df8ae355ca0eb6e405243145e5e9d3e965013948/68747470733a2f2f716c74792e73682f67682f476574444b414e2f70726f6a656374732f6d6f636b2d636861696e2f6d61696e7461696e6162696c6974792e737667)](https://qlty.sh/gh/GetDKAN/projects/mock-chain)[![Code Coverage](https://camo.githubusercontent.com/e13a9ae9565f884220fd9302d8d04df9f295df69db717f659ad09662e1b1902a/68747470733a2f2f716c74792e73682f67682f476574444b414e2f70726f6a656374732f6d6f636b2d636861696e2f636f7665726167652e737667)](https://qlty.sh/gh/GetDKAN/projects/mock-chain)[![GPLv3 license](https://camo.githubusercontent.com/48bf9b56d44f38db53ce21294cf0b9487d0a3734ab3ba1fe4c69858ae20db2c1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d47504c76332d626c75652e737667)](https://www.gnu.org/licenses/gpl-3.0.en.html)

Create complex mocks/doubles with ease.

### Example

[](#example)

Imagine a chain of methods and objects like this:

```
$body->getSystem('nervous')->getOrgan('brain')->getName();
```

Creating a mock for the body object with phpunit alone might look like this:

```
$organ = $this->getMockBuilder(Organ::class)
    ->disableOriginalConstructor()
    ->onlyMethods(['getName'])
    ->getMock();

$organ->method('getName')->willReturn('brain');

$system = $this->getMockBuilder(System::class)
  ->disableOriginalConstructor()
  ->onlyMethods(['getOrgan'])
  ->getMock();

$system->method('getOrgan')->willReturn($organ);

$body = $this->getMockBuilder(Body::class)
  ->disableOriginalConstructor()
  ->onlyMethods(['getSystem'])
  ->getMock();

$body->method('getSystem')->willReturn($system);
```

The implementation of a simple chain of mocks can become very verbose. The purpose of this library is to make this process simpler. Here is the same mocked object implemented with a mock-chain:

```
$body = (new Chain($this))
    ->add(Body::class, 'getSystem', System::class)
    ->add(System::class, 'getOrgan', Organ::class)
    ->add(Organ::class, 'getName', 'brain')
    ->getMock();
```

Documentation
-------------

[](#documentation)

The majority of the work that can be done with this library happens through a single class: The `Chain` class.

By exploring the few methods exposed by this class, we should be able to understand the full power of the library.

### Mocking an Object and a Single Method

[](#mocking-an-object-and-a-single-method)

With `mock-chain` we can mock an object and one of its methods in a single line of code.

```
$mock = (new Chain($this))
      ->add(Organ::class, "getName", "heart")
      ->getMock();
```

Let's explore what is happening here.

```
(new Chain($this))
```

Here, we are calling the constructor of the `Chain` class to create a `Chain` object. The extra parenthesis around the call to the constructor allow us to immediately start calling methods without keeping a reference of the `Chain` object itself.

The `Chain` class is a "better" interface around the mocking capabilities provided by `phpunit`, but all the mocking power comes from `phpunit`. This is why the constructor of the `Chain` class takes a `PHPUnit\Framework\TestCase` object.

```
->add(Organ::class, "getName", "heart")
```

The `add` method is used to inform the `Chain` object of the structure of the mock or mocks that we wish to create.

The first argument to `add` is the **full name of the class** for which we want to create a mock object. In our example we want to create an `Organ` object.

The class name is the only required parameter in the `add` method, but more often than not we want to mock a call to a method of an object. The extra, optional, parameters allow exactly that.

The second parameter is the **name of a method** in the `Organ` class: `getName`.

The third parameter is what we want the **mocked object to return** when `getName` is called. In our example we want to return the string *"heart"*.

Finally,

```
->getMock()
```

returns the mock object constructed by the `Chain` class.

We can easily check in a test that our mock object is working as expected:

```
$this->assertEquals("heart", $mock->getName());
```

### Mocking an Object and Multiple Methods

[](#mocking-an-object-and-multiple-methods)

To mock multiple methods, we simply call `add` multiple times.

```
$mock = (new Chain($this))
        ->add(Organ::class, "getName", "heart")
        ->add(Organ::class, "shoutName", "HEART")
        ->getMock();
```

`Chain` assumes that each class name is used to generate a single mock object of that class. So, this chain does not create two mock `Organ` objects, but a single `Organ` object with both `getName` and `shoutName` mocked.

Because it is common to mock multiple methods for a single object, the `Chain` class provides a method to make this operation less verbose: `addd` *(with three Ds)*.

With the `addd` method we can simplify our example like this:

```
$mock = (new Chain($this))
        ->add(Organ::class, "getName", "heart")
        ->addd("shoutName", "HEART")
        ->getMock();
```

When `addd` is used, the `Chain` assumes that the method is a mock of whatever the last named class was before `addd` was called. In our case it is the `Organ` class.

The impact is very subtle, but we have found that in complex mocks, using `addd` also provides a visual break to easily see the different types of objects being mocked.

### Returning Mocks

[](#returning-mocks)

The third parameter of the `add` method can be given anything to be return by the mocked method: strings, arrays, objects, booleans, etc.

We can even return another mocked object. Addressing this scenario is the main reason this library exist, and why it is called `mock-chain`: We want to be able to define chains of mocked objects and methods easily.

To accomplish our goal we simply return the class name of the mock object we want to return.

```
$mock = (new Chain($this))
        ->add(System::class, "getOrgan", Organ::class)
        ->add(Organ::class, "getName", "heart")
        ->addd("shoutName", "HEART")
        ->getMock();
```

It is important to note that in this new example the **main** mock object returned by `getMock` is of the `System` class. Whatever the first named class that is registered with the `Chain` is, becomes the **root** of the chain. Any other mocks will only be accessible through interactions with the **root** object.

A second mock object of class `Organ` is also being defined, and it is accessible through the `getOrgan` method from the mocked `System` object.

Given this structure, we can make assertions across our mocks:

```
$this->assertEquals("heart",
    $mock->getOrgan("blah")->getName());
```

### Mocking Different Returns with Sequences

[](#mocking-different-returns-with-sequences)

Through some paths of our code, we might need the same mocked object to respond differently under different circumstances. There are multiple ways to accomplish this with `mock-chain`, but the simplest way is to use the `Sequence` class.

A `Sequence` allows us to define a number of things that should be returned, in order, every time a method is called.

```
$organNames = (new Sequence())
        ->add("heart")
        ->add("lungs");

$mock = (new Chain($this))
  ->add(Organ::class, "getName", $organNames)
  ->getMock();

$this->assertEquals("heart", $mock->getName());
$this->assertEquals("lungs", $mock->getName());
```

In this example we are creating a `Sequence` of organ names, and we are telling the chain that this sequence of things should be returned when the `getName` method in our `Organ` mock is called.

Our assertions confirm the expected behavior by showing that *"heart"* is returned when `getName` is first called, and *"lungs"* when `getName` is called a second time. If `getName` was to be called a third or fourth time, *"lungs"* would be returned again.

Similarly to how we can return anything from mocked methods, including other mocks, we can do the same with sequences.

```
$organs = (new Sequence())
        ->add(Organ::class)
        ->add("lungs");

$mock = (new Chain($this))
  ->add(System::class, "getOrgan", $organs)
  ->add(Organ::class, "getName", "heart")
  ->getMock();

$this->assertEquals("heart", $mock->getOrgan("blah")->getName());
$this->assertEquals("lungs", $mock->getOrgan("blah"));
```

Here we are returning a mock of `Organ` as the first element of the sequence, and a string as the second without any issues.

### Mocking Different Returns with Options

[](#mocking-different-returns-with-options)

`Options` give us a bit more power than `Sequence` by allowing us to take into account the input to the mocked methods as we decide what should be returned.

```
$organs = (new Options())
  ->add("heart", Organ::class)
  ->add("lungs", "yep, the lungs");

$mock = (new Chain($this))
  ->add(System::class, "getOrgan", $organs)
  ->add(Organ::class, "getName", "heart")
  ->getMock();

$this->assertEquals("yep, the lungs",
    $mock->getOrgan("lungs"));
$this->assertEquals("heart",
    $mock->getOrgan("heart")->getName());
```

In this `Options` object we are defining that a call to `getOrgan` with an input of *"hearts"* should return our `Organ` mock, but a call to `getOrgan` with an input of *"lungs"* should return the string *"yep, the lungs"*. Notice in the assertions that the order of the options does not matter.

If we are dealing with more complex methods that take multiple inputs/arguments, `Options` have two mechanisms to deal with these scenarios: index and JSON string.

#### Index

[](#index)

```
$organs = (new Options())
  ->add("heart",Organ::class)
  ->add("lung", "yep, the left lung")
  ->index(0);

$mock = (new Chain($this))
  ->add(System::class, "getOrganByNameAndIndex", $organs)
  ->add(Organ::class, "getName", "heart")
  ->getMock();

$this->assertEquals("yep, the left lung",
  $mock->getOrganByNameAndIndex("lung", 0));
$this->assertEquals("heart",
  $mock->getOrganByNameAndIndex("heart", 0)->getName());
```

In this example we have a more complex method `getOrganByNameAndIndex` that takes 2 arguments: an **organ name** and an **index**. If during the process of mocking we determine that we only care about one of the arguments to our method, we could model that by using the `index` method of the `Options` class. In this example, we are describing that we only care about the first argument, the **organ name**, when determining what to return.

#### JSON string

[](#json-string)

```
$organs = (new Options())
  ->add(json_encode(["lung", 0]),"yep, the left lung")
  ->add(json_encode(["lung", 1]), "yep, the right lung");

$mock = (new Chain($this))
  ->add(System::class, "getOrganByNameAndIndex", $organs)
  ->add(Organ::class, "getName", "heart")
  ->getMock();

$this->assertEquals("yep, the left lung",
  $mock->getOrganByNameAndIndex("lung", 0));
$this->assertEquals("yep, the right lung",
  $mock->getOrganByNameAndIndex("lung", 1));
```

When we have complex methods with multiple arguments that we want to take into account when making decisions about what to return, we can always create a JSON string of an array representing the inputs to our method.

In our example, when the inputs to `getOrganByNameAndIndex` are *"lung"* and 0, we want to return *"yep, the left lung"*. But, if the inputs to our method are *"lung"*, and *1*, we would like to return *"yep, the right lung"*.

###  Health Score

58

—

FairBetter than 98% of packages

Maintenance78

Regular maintenance activity

Popularity35

Limited adoption so far

Community19

Small or concentrated contributor base

Maturity84

Battle-tested with a long release history

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~152 days

Recently: every ~180 days

Total

15

Last Release

133d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/be88bcbccb292750a3d5e1cace823de415dbdd05682f685ebc24beb6b8c06e84?d=identicon)[fmizzell](/maintainers/fmizzell)

---

Top Contributors

[![fmizzell](https://avatars.githubusercontent.com/u/5494584?v=4)](https://github.com/fmizzell "fmizzell (24 commits)")[![paul-m](https://avatars.githubusercontent.com/u/360238?v=4)](https://github.com/paul-m "paul-m (15 commits)")[![dafeder](https://avatars.githubusercontent.com/u/309671?v=4)](https://github.com/dafeder "dafeder (8 commits)")[![janette](https://avatars.githubusercontent.com/u/314172?v=4)](https://github.com/janette "janette (1 commits)")[![kaise-lafrai](https://avatars.githubusercontent.com/u/56809719?v=4)](https://github.com/kaise-lafrai "kaise-lafrai (1 commits)")[![thierrydallacroce](https://avatars.githubusercontent.com/u/729791?v=4)](https://github.com/thierrydallacroce "thierrydallacroce (1 commits)")

###  Code Quality

Static AnalysisRector

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/getdkan-mock-chain/health.svg)

```
[![Health](https://phpackages.com/badges/getdkan-mock-chain/health.svg)](https://phpackages.com/packages/getdkan-mock-chain)
```

###  Alternatives

[ramsey/devtools

A Composer plugin to aid PHP library and application development.

7134.7k26](/packages/ramsey-devtools)[jimbojsb/workman

PHP process forking &amp; daemonizing library

608.8k](/packages/jimbojsb-workman)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
