PHPackages                             substrakt/faktory - 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. [Testing &amp; Quality](/categories/testing)
4. /
5. substrakt/faktory

ActiveLibrary[Testing &amp; Quality](/categories/testing)

substrakt/faktory
=================

WordPress factories for Test-driven development.

v1.0.2(2y ago)9104BSD-3-ClausePHP

Since Nov 8Pushed 1y ago2 watchersCompare

[ Source](https://github.com/substrakt/faktory)[ Packagist](https://packagist.org/packages/substrakt/faktory)[ RSS](/packages/substrakt-faktory/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (3)Versions (4)Used By (0)

Faktory
=======

[](#faktory)

Faktory is a library for creating fixtures for WordPress. It can be used to create Posts, Pages, Custom Post Types, and Terms. It is particularly useful for generating fixtures during test-driven development.

Faktory ships with factories defined for pages, posts, terms and custom post types. You can define your own factories by simply creating a corresponding PHP file and including the default properties.

To create the fake data, Faktory uses [Rando-PHP](https://github.com/Hi-Folks/rando-php).

Creating a factory
------------------

[](#creating-a-factory)

```
$f = Faktory::create(string $factory = "page", array|object $args = []);

```

### Parameters

[](#parameters)

#### factory

[](#factory)

The name of the factory to create. The casing must match the casing used for the filename of the factory. This is to avoid issues with case sensitive file systems such as Linux. The builtin factory files are all lowercase.

- page (default)
- post

#### args

[](#args)

An array of elements to overwrite the default values. The array for the page and post factories is passed to [`wp_insert_post`](https://developer.wordpress.org/reference/functions/wp_insert_post/) to save the post to the database.

```
$args = [
    "comment_count"     => "",
    "comment_status"    => "",
    "ID"                => 0,
    "meta_input"        => [],
    "menu_order"        => 0,
    "ping_status"       => "",
    "post_author"       => "",
    "post_content"      => "",
    "post_date_gmt"     => $date,
    "post_date"         => $date,
    "post_excerpt"      => "",
    "post_modified_gmt" => $date,
    "post_modified"     => $date,
    "post_name"         => "",
    "post_parent"       => 0,
    "post_password"     => "",
    "post_status"       => "publish",
    "post_title"        => Randomize::chars()->generate(),
    "post_type"         => "page",
    "tax_input"         => [],
];

```

**Note:** the field `post_type` will be set to `post` when creating a new factory from `Faktory::create("post")`. The ID of the inserted item will be updated to match that of the corresponding row in the database.

### Return values

[](#return-values)

A `Faktory\Page` object will be returned.

### Examples

[](#examples)

#### Creating a page factory

[](#creating-a-page-factory)

```
$f = Faktory::create("page");

```

#### Creating a page factory setting some attributes

[](#creating-a-page-factory-setting-some-attributes)

```
$f = Faktory::create("page", ["post_title" => "About me", "post_name" => "about"]);

```

Creating a factory for a term
-----------------------------

[](#creating-a-factory-for-a-term)

The same method `Faktory::create` can be used to create term fixtures. The deafult factory is page so make sure the first parameter is set to term.

#### args

[](#args-1)

An array of elements to overwrite the default values. The array is passed as the `$args` parameter to [`wp_insert_term`](https://developer.wordpress.org/reference/functions/wp_insert_term/) to save the term to the database.

```
$args = [
    "alias_of"    => "",
    "description" => "",
    "name"        => Randomize::chars()->generate(),
    "parent"      => 0,
    "slug"        => "",
    "taxonomy"    => "category",
];

```

### Return values

[](#return-values-1)

A `Faktory\Term` object will be returned.

### Examples

[](#examples-1)

#### Creating a category term

[](#creating-a-category-term)

```
$f = Faktory::create("term", ["name" => "Programming"]);

```

#### Creating a tag term

[](#creating-a-tag-term)

```
$f = Faktory::create("term", ["name" => "TDD", "taxonomy" => "post_tag"]);

```

#### Creating a term for a custom taxonomy called "genre"

[](#creating-a-term-for-a-custom-taxonomy-called-genre)

```
$f = Faktory::create("term", ["name" => "Computer Science", "taxonomy" => "genre"]);

```

#### Using the terms with another factory

[](#using-the-terms-with-another-factory)

```
$term = Faktory::create("term", ["name" => "Programming"]);
$page = Faktory::create("page", [
    "tax_input" => [
        "category" => [$term->ID]
    ]
]);

```

As per the WordPress documentation if the taxonomy is hierarchical, the term list needs to be either an array of term IDs or a comma-separated string of IDs. If the taxonomy is non-hierarchical, the term list can be an array that contains term names or slugs, or a comma-separated string of names or slugs. This is because, in hierarchical taxonomy, child terms can have the same names with different parent terms, so the only way to connect them is using ID. Default empty.

Using `tax_input` is the equivalent to calling `wp_set_post_terms()` for each custom taxonomy in the array. If the current user doesn’t have the capability to work with a taxonomy, then you must use `wp_set_object_terms()` instead.

Setting the user the test are run against to an administrator will allow `tax_input` to be used. The following example is taken from the bootstrap file from the test suit at Substrakt.

```
(function() {
    $userId = 1;
    if (empty(get_users())) {
        $userId = wp_create_user('admin', 'password');
    }
    $user = new \WP_User($userId);
    $user->set_role('administrator');
    \wp_set_current_user($userId);
})();

```

Creating factories without saving them
--------------------------------------

[](#creating-factories-without-saving-them)

If you want to create a factory but not have it saved to the database then you can use the `new` method in place of the `create` method. The `new` method takes the same arguments as the `create` method.

```
$f = Faktory::new("page");

```

If you want to save the Faktory afterall then the `save` method can be called.

```
$f = Faktory::new("page");
...
$f->save();

```

Pass the returned factory to a class
------------------------------------

[](#pass-the-returned-factory-to-a-class)

`Faktory::create` or `Faktory::new` will return an object. This object mimics a WordPress post object or term object. You may find yourself frequently passing the created fixture as a paramter to another class. This is where the `as` method can come in useful.

```
$f = Faktory::create("page")->as("TheClassIWant");
# is equal to
$f = new TheClassIWant(Faktory::create("page"));

```

Creating multiple factories at once
-----------------------------------

[](#creating-multiple-factories-at-once)

`Faktory::createMany` or `Faktory::newMany` will return an array of objects in a single line of code.

```
$factories = Faktory::createMany(5, "post");

```

`createMany` will save the objects to the database. `newMany` will not save the objects to the database.

They both support the following args:

- `count` (default: `2`): The number of objects to create.
- `type` (default: `page`): See [creating a page factory](https://github.com/substrakt/faktory?tab=readme-ov-file#creating-a-page-factory).
- `args` (default: `[]`): See [creating a page factory and setting attributes](https://github.com/substrakt/faktory?tab=readme-ov-file#creating-a-page-factory-setting-some-attributes).
- `class` (default `null`): The string of a class to instantiate the factories as.

Each Factory is given an incrementing suffix to the `post_title` and `post_name`. See the following example for context.

### Examples

[](#examples-2)

Basic use-case:

```
$fs = Faktory::createMany(3, 'post', ['post_title' => 'Foo'], 'My\Desired\Class');

```

The above will:

- Create 3 factories.
- Assign them the `post_titles` of `Foo 0`, `Foo 1`, &amp; `Foo 2`.
- Instantiate them as `My\Desired\Class`.
- Save them to the database.
- Return the results as an array.

Creating your own factories
---------------------------

[](#creating-your-own-factories)

Add a directory called `factories` in your desired location. In this directory you will put all your custom factory files. If you create a file called, post.php, page.php or term.php it will overwrite the default factories used by Faktory. This is perfectly acceptable.

You will then need to register the directory with Faktory using the `addDirs` method. This should be done in your bootstrap file before you intend to use Faktory.

### Example

[](#example)

```
Faktory::addDirs([
    "the/full/path/to/your/factories/dir"
]);

```

Your factories need to return an array of data. The data that is returned depends on the type of data that is being inserted into the database. Currently Faktory supports post types and terms. To inform Faktory what type some object is to be created use the [class attribute](#the-class-attribute) with your returned array. For example see the [default factories](#default-factories).

### The class attribute

[](#the-class-attribute)

The class attribute defined on a factory determines which Faktory object will be returned. Currently this is either a `Faktory\Post`, `Faktory\Page` or a `Faktory\Term`. If no class attribute is present a `Faktory\Page` is returned.

```
[
...,
"class" => "\Faktory\Page",
];

```

The `Faktory\Page` and `Faktory\Post` objects are identical other than in name. They exist as a way to help distinquish between the two post types. If you are creating term factories then it will be important to specific the term class.

```
[
...,
"class" => "\Faktory\Term",
];

```

Each of the classes has a save method which is responsible for inserting the data into the database if `Faktory::create` was used.

Default factories
-----------------

[](#default-factories)

### Page

[](#page)

```
# faktory/factories/page.php
$date = date("Y-m-d H:i:s");
return [
    "comment_count"     => "",
    "comment_status"    => "",
    "ID"                => 0,
    "meta_input"        => [],
    "menu_order"        => 0,
    "ping_status"       => "",
    "post_author"       => "",
    "post_content"      => "",
    "post_date_gmt"     => $date,
    "post_date"         => $date,
    "post_excerpt"      => "",
    "post_modified_gmt" => $date,
    "post_modified"     => $date,
    "post_name"         => "",
    "post_parent"       => 0,
    "post_password"     => "",
    "post_status"       => "publish",
    "post_title"        => Randomize::chars()->generate(),
    "post_type"         => "page",
    "tax_input"         => [],
    "class"             => "\Faktory\Page",
];

```

### Post

[](#post)

```
# faktory/factories/post.php
$date = date("Y-m-d H:i:s");
return [
    "comment_count"     => "",
    "comment_status"    => "",
    "ID"                => 0,
    "meta_input"        => [],
    "menu_order"        => 0,
    "ping_status"       => "",
    "post_author"       => "",
    "post_content"      => "",
    "post_date_gmt"     => $date,
    "post_date"         => $date,
    "post_excerpt"      => "",
    "post_modified_gmt" => $date,
    "post_modified"     => $date,
    "post_name"         => "",
    "post_parent"       => 0,
    "post_password"     => "",
    "post_status"       => "publish",
    "post_title"        => Randomize::chars()->generate(),
    "post_type"         => "post",
    "tax_input"         => [],
    "class"             => "\Faktory\Post",
];

```

### Term

[](#term)

```
# faktory/factories/term.php
return [
    "count"            => 0,
    "description"      => "",
    "name"             => Randomize::chars()->generate(),
    "parent"           => 0,
    "post_author"      => "",
    "slug"             => "",
    "taxonomy"         => "category",
    "term_group"       => 0,
    "term_id"          => 0,
    "term_taxonomy_id" => 0,
    "class"            => "\Faktory\Term",
];

```

Shorthand properties
--------------------

[](#shorthand-properties)

For factories that are a post type (i.e posts, pages etc) shorthand can be used for the array keys. Below is a map showing the what the shorthand property will be transformed into.

```
Faktory::new("page", ["title" => "Foo", "content" => "Foo bar baz"]);

```

### Shorthand keys

[](#shorthand-keys)

Here is a list of the all the shorthand keys and the name of the full key they map to.

```
$map = [
    "author"         => "post_author",
    "name"           => "post_name",
    "type"           => "post_type",
    "title"          => "post_title",
    "date"           => "post_date",
    "date_gmt"       => "post_date_gmt",
    "content"        => "post_content",
    "excerpt"        => "post_excerpt",
    "status"         => "post_status",
    "password"       => "post_password",
    "parent"         => "post_parent",
    "modified"       => "post_modified",
    "modified_gmt"   => "post_modified_gmt",
    "meta"           => "meta_input",
];

```

Undefined factories
-------------------

[](#undefined-factories)

Creating an undefined factories will create a factory based on `page` but the post\_type will be set to what was asked for.

### Example

[](#example-1)

```
$fixture = Faktory::create('foobar');
$fixture->post_type #=> 'foobar'

```

This allow you to easily create fixtures for custom post types without the need to create a corresponding file in your factories directory.

Advanced Custom Field
---------------------

[](#advanced-custom-field)

Currently support for Advanced Custom Fields is provided through the `meta_input` argument.

```
$fixture = Faktory::create('page', [
    'meta_input' => [
        'masthead_image__color'  => '#000',
        '_masthead_image__color' => 'field_5be99d501d261',
    ]
]);

```

Note the field reference (field\_5be99d501d261) is not nessecary for primitive value types, but if you want ACF to return a different value type to what it stores then it is required. An example being ACF stores an integer for the **post\_object** field but returns an instance of `WP_Post`.

If you are having to do alot of `meta_input` work inside your tests, consider moving it to a factory file instead.

Contributing
------------

[](#contributing)

We'd love you to help out with Faktory and no contribution is too small. We particularly welcome pull requests from anyone who is looking to make their first open source contribution.

Fork the Faktory and create a pull request with your changes or features in. Of course, you'll need to include some tests if the changes are to the code.

### Ideas for pull requests

[](#ideas-for-pull-requests)

- Factory for Users
- Factory for Attachments. WordPress stores attachments as posts but requires extra data to make them works as attachments.
- Option to return the native `WP_Post` and `WP_Term` objects instead of the Faktory ones.
- Documentation. You can never have enough examples.

We loosely follow the [PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standards, but we'll probably merge any code that looks close enough.

License
-------

[](#license)

Faktory is licensed under [The BSD 3-Clause License](LICENSE).

---

Development of Faktory is sponsored by [Substrakt](https://substrakt.com)

###  Health Score

29

—

LowBetter than 60% of packages

Maintenance38

Infrequent updates — may be unmaintained

Popularity13

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 50% of commits — single point of failure

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 ~3 days

Total

3

Last Release

907d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/59bf1e58e43af1730ec70f26c9213a77981c94ee5e34b9f4136cf491e2eb7b94?d=identicon)[substrakt](/maintainers/substrakt)

![](https://www.gravatar.com/avatar/06894075bd90e38d8abfb80d04c8357fb284534b9667b0fd820b05672c95ad8d?d=identicon)[Balteg](/maintainers/Balteg)

---

Top Contributors

[![jmwhitworth](https://avatars.githubusercontent.com/u/45968087?v=4)](https://github.com/jmwhitworth "jmwhitworth (1 commits)")[![stuartmaynes](https://avatars.githubusercontent.com/u/1974326?v=4)](https://github.com/stuartmaynes "stuartmaynes (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/substrakt-faktory/health.svg)

```
[![Health](https://phpackages.com/badges/substrakt-faktory/health.svg)](https://phpackages.com/packages/substrakt-faktory)
```

###  Alternatives

[phpspec/prophecy

Highly opinionated mocking framework for PHP 5.3+

8.5k551.7M679](/packages/phpspec-prophecy)[vimeo/psalm

A static analysis tool for finding errors in PHP applications

5.8k77.5M6.7k](/packages/vimeo-psalm)[brianium/paratest

Parallel testing for PHP

2.5k118.8M753](/packages/brianium-paratest)[beberlei/assert

Thin assertion library for input validation in business models.

2.4k96.9M571](/packages/beberlei-assert)[mikey179/vfsstream

Virtual file system to mock the real file system in unit tests.

1.4k108.0M2.7k](/packages/mikey179-vfsstream)[orchestra/testbench

Laravel Testing Helper for Packages Development

2.2k39.1M32.0k](/packages/orchestra-testbench)

PHPackages © 2026

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