What is the best way to create a factory for unit test objects?

If I undertsand correctly, what you are searching for is a good library for creating stubs and mocks.

If you use PHPUnit, it has a features for that. See https://phpunit.de/manual/current/en/test-doubles.html#test-doubles

For example, let’s assume you have a class like this:

namespace MyApp;

class MyCustomUser {

   public function __construct(\WP_User $user) {
     $this->user = $user;
   }

   public function hasCapability($capability) {
      return $this->user->can($capability);
   }
}

You could create a trait that holds an user factory and make use of mocking feature of PHPUnit:

namespace MyApp\Tests;

trait CreateUserTrait {

   protected function createUser(array $capabilitites) {

      $stub = $this->createMock(MyCustomUser::class);

      $has_capability = function($capability) use ($capabilitites) {
         return in_array($capability, $capabilitites, true);
      };

      $stub->method('hasCapability')->will($this->returnCallback($has_capability));
   }
}

At this point in your test classes you can use that trait and make use of the factory:

namespace MyApp\Tests;

use PHPUnit\Framework\TestCase;

class UserMockTest extends TestCase
{
    use CreateUserTrait;

    public function testUserFactoryWorks()
    {
        $userMock = $this->createUser(['create_post']);

        $this->assertTrue($userMock->hasCapability('create_post'));
    }
}

Of course, mocks and stubs are useful whan you need to test other objects that make use of mocked object. You won’t mock the SUT.

A nice side effect of using the mock is we did not used any WordPress function or class in the test (even if the original user object maks use of WordPress WP_User object), so the test could be ran without loading WordPress, making it a real unit test: if you load WordPress it becomes an integration test, not an unit test.

Many people find the mocking syntax of PHP a bit hard to digest. If you are one of them, you might want to have a look at Mockery, which has a simpler API.

Depending on your needs, Faker might also be of great help.


Note: code above requires PHP 5.5 and the PHPUnit version assumed is version 5.* which requires PHP 5.6+