Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for creating an Auditable model even if application is not spun up #962

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

cosmastech
Copy link
Contributor

@cosmastech cosmastech commented Aug 7, 2024

As our team focuses on creating more testable components, we prefer unit tests over feature tests. Even better for those unit tests if they don't require the Laravel framework to be booted at all.

As is the case for most Laravel applications, Models are everywhere in our codebase. Sometimes we only want to test very limited things about a model behavior, or a function that takes in a model, but only needs a property or two from it. If we try to write a unit test (extending from PHPUnit\Framework\TestCase), we cannot even call new User(['id' => 1]) because as Auditable boots, it attempts to access the App facade (which won't be set, and will raise an exception).

For instance

Consider the following example

namespace App\Models;

use OwenIt\Auditing\Contracts\Auditable;
use Illuminate\Database\Eloquent\Model;

class User extends Model implements Auditable
{
    use \OwenIt\Auditing\Auditable;

    protected $fillable = ['user_type'];

    public function isAdmin(): bool
    {
        return $this->user_type === 'admin';
    }
}

and a unit test

class UserTest extends \PHPUnit\Framework\TestCase
{
    public function test_adminUserType_returnsIsAdminTrue(): void
    {
        $user = new User(['user_type' => 'admin']);
        $this->assertTrue($user->isAdmin());
    }
}

In the current version of Auditable, this will fail on the first line of the test with a RuntimeException.

The change implemented is to just swallow up this particular exception and not to attempt to add the observer.

Other approaches

Trigger a notice

Instead of swallowing the exception, we could trigger_error(), however, then there's a "notice" attached to the test.
image

Leverage Auditable::isAuditingDisabled()

I tried adding this check into Auditable::isAuditingEnabled(), but it broke tests, indicating a breaking change. Also, it's possible that a model is instantiated when auditing is temporarily disabled, but then when saving or changing the model later in the request, we do want auditing enabled.

@willpower232
Copy link
Contributor

Personally, I'm confused about why you wouldn't always test in the context of the laravel application since if the code in your project is being run without laravel, that sounds like you don't really need laravel 😅 but hey, you do you.

IMHO the only change I would suggest is matching the entire exception message rather than using stripos. According to github, the exception message hasn't changed for 9 years so you would hope it would be an trustworthy string match.

@cosmastech cosmastech force-pushed the allow-auditable-in-unit-tests branch from e7383f6 to d03c37c Compare August 7, 2024 12:30
@cosmastech
Copy link
Contributor Author

IMHO the only change I would suggest is matching the entire exception message rather than using stripos. According to github, the exception message hasn't changed for 9 years so you would hope it would be an trustworthy string match.

Thanks for checking that! I had the thought to check that, but was being lazy. 😝

I have made the change.

Personally, I'm confused about why you wouldn't always test in the context of the laravel application since if the code in your project is being run without laravel, that sounds like you don't really need laravel 😅 but hey, you do you.

It's mainly to avoid the setup/teardown required for Laravel's TestCase. While there's a lot convenience included with that, it's not for free. Each test case has to spin through a lot of extra code by creating, booting, and then destroying the application for something that isn't truly needed by the system under test.

The benefit to austerity in test classes is faster feedback. For a single test, this feedback is probably close to imperceptible (talking a change from 0.02 => 0.01 seconds). But for the test suite as a whole, which is run by every member of the team locally multiple times per day, plus many, many times per day in our CI/CD pipelines, this adds up as the test suite grows.

In general, I hope to write testable components that don't require Models at all, but it's a work in progress on that front. There's a lot of philosophical reasoning that I don't assume every person using Laravel would subscribe to (or even benefit from). Just hoping that this change's risk is low impact enough that it can get added to the package.

@willpower232
Copy link
Contributor

You did also lint the rest of the file so if you could undo that, that would be swell 😅

@cosmastech cosmastech force-pushed the allow-auditable-in-unit-tests branch from d03c37c to 41e4c80 Compare August 7, 2024 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants