Automated Testing: 6 Open-Source Laravel Projects

Recently I became much more interested in automated testing - PHPUnit, TDD, Laravel Dusk, Travis and other keywords became "closer", so I decided to look around how other people perform automated testing. Found quite a few Laravel open-source projects on GitHub, picked 6 of them to review their testing suite. Let's take a look. Disclaimer: I've looked at only full Laravel projects (not packages) and only Laravel 5.3+.

1. Laravel.io portal

URL: https://github.com/laravelio/portal Recently re-launched Laravel.io has its source published on Github. And Dries Vints has done quite a good job on writing tests there. laravel.io tests Laravel.io is using both Feature testing and Component (more like Unit) testing. What's interesting is that the same or similar behavior is tested in both cases. Example 1 - tests/Feature/ReplyTest.php
    public function users_can_add_a_reply_to_a_thread()
    {
        factory(Thread::class)->create(['subject' => 'The first thread', 'slug' => 'the-first-thread']);
        $this->login();
        $this->visit('/forum/the-first-thread')
            ->type('The first reply', 'body')
            ->press('Reply')
            ->see('The first thread')
            ->see('The first reply')
            ->see('Reply successfully added!');
    }
Example 2 - tests/Components/Jobs/CreateReplyTest.php
    public function we_can_create_a_reply()
    {
        $job = new CreateReply('Foo', '', $this->createUser(), factory(Thread::class)->create());
        $this->assertInstanceOf(Reply::class, $job->handle());
    }
This is actually quite a good thing: it tests both Jobs layer and actually clicking things and using it in the browser. Also what I've noticed is that main Laravel.io has been upgraded to Laravel 5.4, but test suite remained in 5.3-style, with BrowserKitTestCase implementation. Nothing wrong with that, just a notice. Finally, this project also uses continuous integration with Travis, and later I've noticed that most projects use it too.
Have you tried our tool to generate Laravel adminpanel without a line of code? Go to QuickAdminPanel.com

2. Cachet - an open source status page system

URL: https://github.com/cachethq/Cachet Led by James Brooks and Graham Campbell, this project has a massive test suite. It's even hard to understand by looking at the surface. cachet tests So, where do I start here... Actually, I won't even dig deeper into the testing logic of this project, cause it is really deep. Here's an example - tests/Models/ComponentTest.php:
use AltThree\TestBench\ValidationTrait;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Tests\Cachet\AbstractTestCase;

class ComponentTest extends AbstractTestCase
{
    use ValidationTrait;
    public function testValidation()
    {
        $this->checkRules(new Component());
    }
}
Ok, so there's ValidationTrait being used, then some AbstractTestCase. And this logic is all over tests - some abstracted "magic" which is doing all the work. And I'm not saying it is a bad thing - pretty sure it works damn well under the hood. It's just not easy to learn and follow from this test suite. But if someone wants to dig deeper - good luck!

3. October CMS

URL: https://github.com/octobercms/october No.1 Laravel-based CMS on the market, and it has pretty good testing suite. octobercms tests First thing - tests folder has a really informative readme.md file, specifically for testing process. All testing in October CMS consists of:
  • Unit testing
  • Functional testing
  • Plugin testing
And each of these "zones" has its own base class to extend - there's TestCase, UiTestCase and PluginTestCase. The logic is also quite complicated and abstracted - here's an example tests/unit/backend/models/ExportModelTest.php:
class ExportModelTest extends TestCase
{
    //
    // Helpers
    //
    protected static function callProtectedMethod($object, $name, $params = [])
    {
        $className = get_class($object);
        $class = new ReflectionClass($className);
        $method = $class->getMethod($name);
        $method->setAccessible(true);
        return $method->invokeArgs($object, $params);
    }
    //
    // Tests
    //
    public function testEncodeArrayValue()
    {
        $model = new ExampleExportModel;
        $data = ['foo', 'bar'];
        $result = self::callProtectedMethod($model, 'encodeArrayValue', [$data]);
        $this->assertEquals('foo|bar', $result);
        $data = ['dps | heals | tank', 'paladin', 'berserker', 'gunner'];
        $result = self::callProtectedMethod($model, 'encodeArrayValue', [$data]);
        $this->assertEquals('dps \| heals \| tank|paladin|berserker|gunner', $result);
        $data = ['art direction', 'roman empire', 'sci-fi'];
        $result = self::callProtectedMethod($model, 'encodeArrayValue', [$data, '-']);
        $this->assertEquals('art direction-roman empire-sci\-fi', $result);
    }
}
As you can see, there's a static helper method (which is, by the way, repeating in other classes, too), then getting class/method and calling it. I'm sure that the creators understand this logic immediately, but it's hard for outsiders. What's also interesting, OctoberCMS uses Selenium for some features: setup documentation is mentioned in tests/readme.md file.

4. Orgmanager - Invite System for GitHub Organizations

URL: https://github.com/orgmanager/orgmanager This is quite a simple project by Miguel Piedrafita, Orgmanager's tests are also pretty simple and understandable. Also divided into Unit, Feature and API tests. orgmanager tests What I've seen here is an interesting case - calling Artisan commands from tests, like unit/JoinTest.php:
    public function testJoinCommand()
    {
        $user = factory(User::class)->create();
        $org = factory(Org::class)->create([
          'userid' => $user->id,
        ]);
        Github::shouldReceive('authenticate')
                  ->once()
                  ->with($org->user->token, null, 'http_token')
                  ->andReturn();
        Artisan::call('orgmanager:joinorg', [
          'org'      => $org->id,
          'username' => $user->github_username,
        ]);
        $this->assertEquals($user->github_username.' was invited to '.$org->name."\n", Artisan::output());
    }
Calling artisan command and asserting its output - pretty interesting. I'm sure it works, but really non-standard way.

5. PHPMap

URL: https://github.com/PHPMap/phpmap Created and maintained by Florian Wartner. phpmap testing PHPMap has a tests suite which reminds of the standards shown by Laracasts or Test-Driven Laravel course. Here's an example of Feature/FavoritesTest.php:
    public function guests_can_not_favorite_anything()
    {
        $this->withExceptionHandling()
            ->post('forum/replies/1/favorites')
            ->assertRedirect('/login');
    }

    public function an_authenticated_user_can_favorite_any_reply()
    {
        $this->signIn();
        $reply = create('App\Models\Forum\Reply');
        $this->post('forum/replies/'.$reply->id.'/forum/favorites');
        $this->assertCount(1, $reply->favorites);
    }
PHPMap's tests are divided into Unit, Feature and... wait for it... Laravel Dusk! Finally I've found the project which actually uses Dusk in production. Here's how it looks - tests/Browser/MapTest.php:
    public function testMap()
    {
        $this->browse(function ($browser) {
            $browser->visit('/map')
                    ->assertSee('PHPMap');
        });
    }

6. Timegrid - Free, open-source, online appointments platform

URL: https://github.com/timegridio/timegrid The biggest contributor to Timegrid is Ariel Vallese, and he's done pretty awesome job with tests here. timegrid testing There are just a lot of tests here: Unit, Acceptance and Integration, and every folder has a few sub-folders deeper. Here's an example - acceptance/scenarios/consulting/ConsultingScenarioTest.php:
    public function it_fits_for_consulting_scenario()
    {
        $this->arrangeScenario();
        $this->the_business_publishes_a_consulting_service();
        $this->the_business_publishes_vacancies();
        $this->a_user_subscribes_to_business();
        $this->the_user_queries_vacancies();
        $this->it_provides_available_times_for_requested_service_date();
        $this->the_user_takes_a_reservation();
        $this->the_user_sees_the_reservation_ticket();
    }

    public function the_business_publishes_a_consulting_service()
    {
        $this->service = $this->makeService([
            'name'     => 'OnSite 4hs Support',
            'duration' => 60 * 4,
            ]);
        $this->actingAs($this->owner);
        $this->call('POST', route('manager.business.service.store', $this->business), $this->service->toArray());
        $this->assertCount(1, $this->business->fresh()->services);
    }
One all-in-one method and then some more test cases one by one. The official stats in the repository look pretty good: 89% Tests Coverage. Finally, what is interesting, author is also testing migrations, here's tests/unit/migration/MigrationTest.php:
    public function it_refreshes_rollbacks_and_seeds_the_database()
    {
        $database = env('DB_CONNECTION');
        $this->assertNotNull($database);
        $exitCode = Artisan::call('migrate:refresh', ['--database' => $database]);
        $this->assertEquals(0, $exitCode);
        $exitCode = Artisan::call('migrate:rollback', ['--database' => $database]);
        $this->assertEquals(0, $exitCode);
        $exitCode = Artisan::call('migrate', ['--database' => $database]);
        $this->assertEquals(0, $exitCode);
        $exitCode = Artisan::call('db:seed', ['--database' => $database]);
        $this->assertEquals(0, $exitCode);
    }
Using Artisan commands in tests might not be the greatest design pattern, but it simply tests one of the most important functionality of any web app.

Overall Conclusions

After looking at all of these different projects (and some I haven't mentioned here for various reasons), here are main takeaways I've made to myself about testing:
  • There's no choice between Unit "or" Feature testing - most of the projects have BOTH, or even more types of tests;
  • Majority of projects use continuous integration (usually Travis) with test suite - otherwise, why bother writing tests?
  • There are a lot of different ways to structure the tests - it totally depends on the project, there's no "silver bullet";
  • Also a lot of ways to group internal testing functionality - helpers, abstract classes, seeding data etc. No rules, stick to what works for you.
  • Migration to newer Laravel version may be pretty painful - for example, 5.3 tests look different from 5.4 version. So you need to think ahead about updates.
  • Same thought from different angle - as your application grows, you would have to come back and change/add tests. In these projects I "feel" some legacy code, just because of some tests not being used anymore.
That's it from me, anything you would add to the list of open-source projects to learn testing from?

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 58 courses (1054 lessons, total 46 h 42 min)
  • 78 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials