javadihugo's avatar

Testing usage of string validation rule

My controller applies the string validation rule to name.

test('name must be a string', function ($name) {
    $user = User::factory()->create();

    $response = $this
        ->actingAs($user)
        ->post('/organizations', ['name' => $name]);

    $response->assertInvalid(['name' => 'The name field must be a string.']);
})->with([
    true,
    1,
    1.1,
]);

What other incorrect types should I test for (e.g. array, object, etc.)? Am I taking this too far?

0 likes
8 replies
LaryAI's avatar
Level 58

Testing your validation rules thoroughly is a good practice to ensure that your application behaves correctly with different types of input. In the case of the string validation rule, you are already testing for boolean, integer, and float values. Here are some additional types you might want to test:

  • Array
  • Object
  • Null
  • JSON string (which is technically a string, but you might want to ensure it's treated as invalid if you expect a plain string)

Here's how you could extend your test to include these cases:

test('name must be a string', function ($name) {
    $user = User::factory()->create();

    $response = $this
        ->actingAs($user)
        ->post('/organizations', ['name' => $name]);

    $response->assertInvalid(['name']);
})->with([
    true,
    1,
    1.1,
    [],
    new stdClass(),
    null,
    json_encode(['key' => 'value']),
]);

Note that I've changed the assertion to assertInvalid(['name']) because the error message might vary depending on the type of the invalid value, and you're primarily interested in asserting that the name field is invalid, not the specific error message.

Testing for these cases isn't taking it too far if you want to be confident that your application can handle incorrect types gracefully. However, if you have many validation rules and many fields, it might be more practical to focus on the most likely incorrect types that a user might input.

javadihugo's avatar

Running with this for now. Feedback greatly appreciated.

<?php

use App\Models\User;

beforeEach(function () {
    $this->user = User::factory()->create();
    $this->valid = ['name' => fake()->company()];
});

test('name is required', function () {
    $this
        ->actingAs($this->user)
        ->post('/organizations', [])
        ->assertInvalid(['name' => 'is required']);
});

test('name must be a string', function () {
    $this
        ->actingAs($this->user)
        ->post('/organizations', ['name' => 1])
        ->assertInvalid(['name' => 'must be a string']);
});

test('name must not be greater than 255 characters', function () {
    $this
        ->actingAs($this->user)
        ->post('/organizations', ['name' => str_repeat('x', 256)])
        ->assertInvalid(['name' => 'must not be greater than 255 characters']);
});

test('organizations can be created', function () {
    $this->assertDatabaseMissing('organizations', $this->valid);

    $this->actingAs($this->user)->post('/organizations', $this->valid);

    $this->assertDatabaseHas('organizations', $this->valid);
});

test('user is assigned admin membership', function () {
    expect($this->user->organizations)->toBeEmpty();

    $this->actingAs($this->user)->post('/organizations', $this->valid);

    expect($this->user->fresh()->organizations->first()->membership->role)
        ->toBe('admin');
});

test('user switches to new organization', function () {
    expect($this->user->currentOrganization)->toBeNull();
    expect($this->user->organizations)->toBeEmpty();

    $this->actingAs($this->user)->post('/organizations', $this->valid);

    expect($this->user->fresh()->currentOrganization->name)
        ->toBe($this->valid['name']);
});

test('user is redirected to dashboard', function () {
    $this
        ->actingAs($this->user)
        ->post('/organizations', $this->valid)
        ->assertRedirect('/');
});
javadihugo's avatar

@Snapey I've been pondering this decision for a while. Part of me thinks we should test that our code applies the correct validation rules to user input. These tests seem to flow out naturally when following TDD too (not that that's necessarily a good reason for their existence.) @lukedowning19 does this in his "build a forum" course. I'm not stuck in my ways about this though and open to hearing the opinions of others. Perhaps there's a better way to test our code applies the correct validation rules to user input. Perhaps it's not worth testing at all. What are your thoughts?

javadihugo's avatar

@Snapey I somehow missed that... Great read/solution. Thanks. I like the author too - I think he will be a great source of knowledge.

Please or to participate in this conversation.