logo
podcast Podcast
get help Get Unstuck

An easy way to test UUIDs

Laravel has you covered

Joel Clermont
Joel Clermont
2024-03-25

Recently I was working on a project where we issue API tokens to "machine users". A machine user represents a remote system that would interact with our API. While it lived in the users table like normal application users, it was set up in such a way that it could only authenticate using the API token. The machine user account had no password and no way to even attempt to reset the password.

Part of this setup involved generating a UUID as part of the machine user's "email address". Again, this wasn't meant to be a real email address. It just uniquely identified this machine user in audit logs.

While writing a test for the command which generates these machine users and issues them an API token, I needed to verify that the email address was generated as expected. Let's say the email looks like this:

machine-user-<uuid>@domain.disabled

How would you write a test to verify that when UUIDs are generated randomly? In the past, I've done this by simply testing the pattern with a regular expression, skipping over the characters from the random UUID.

But this time, I vaguely remembered seeing a recent Twitter exchange where someone mentioned test helpers specificaly for random methods on the Str class.

Sure enough, the Str::uuid docs mention the createUuidsUsing method, which was built for exactly this purpose. Here's how I used it in my test:

public function testSuccess(): void
{
    Str::createUuidsUsing(fn() => '00000000-1111-2222-3333-000000000000');

    $client = Client::factory()->create();

    $this->artisan(GenerateClientApiKey::class, ['id' => $client->id])
        ->assertSuccessful();

    $user = $client->refresh()->users->sole();
    
    Str::createUuidsNormally();
    
    self::assertSame(
        'machine-user-00000000-1111-2222-3333-000000000000@domain.disabled',
        $user->email
    );
    
    // rest of my test assertions here ...
}

By using this helper method, I was able to control the UUID generation in my test. This made it easy to test the exact value, making a cleaner and more readable test than my old regex-based approach.

There is one caution to keep in mind when choosing a fake UUID value to assert against: Don't always use the same value. If every single one of my tests always used the same fake value, it would be possible to have false positives in some edge cases. While it may not be likely, especially if you're using the RefreshDatabase trait, I prefer to play it safe and mix up the fake values from test to test.

Finally, don't forget to reset this test helper with Str::createUuidsNormally. Without it, all the UUIDs generated for the rest of my test suite would continue to match my faked value. In fact, this is why I like to call this clean-up method even before I make my test assertions. In case an assertion fails, I'd prefer to not have the rest of my UUIDs faked if the test suite continues to run.

Hope this helps,

Joel

P.S. If you poke around the Str class, you'll find a few other useful helpers for testing random methods that aren't in the docs. Consider that your homework!

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼
email
No spam. Only real-world advice you can use.