PHPUnit has a setUp method. You should do those things in there. Set the correct app and configs and so on
How can I acceptance test when the service provider bindings are dynamic?
I have a multi-tenant app used by many clients. Lets say the app has an optional "blog" module that can either be enabled or disabled. For some clients it's enabled, for some its disabled. If it's enabled, there is another posts_per_page setting in the same config file. I see two different ways of handling this:
- A
settingstable that would somehow contain the fact thatblogis either enabled disabled (either serialised JSON, or in over two columnssetting, andvalue) and how many posts per page there should be. This would be set via some sort of admin panel in the UI in the application. - A default Laravel config file
config/blog.phpcontaining a settingenabledconfig in which isfalseby default. I detect the client by an environment variable calledCLIENT, which results in all config fromclients/<CLIENT>/config/blog.phpoverriding the main config during application bootstrap. So for the clients that should have the blog module enabled
I've gone ahead and implemented the second option (config driven), but in either case, what I wanted to do is only use these config values in the BlogServiceProvider to set up the necessary bindings. Consider this (highly simplified) example:
// config/blog.php
return [
'enabled' => false,
'posts_per_page' => 10,
];
// clients/client_a/config/blog.php
return [
'enabled' => true, // Client A has overriden the default config to enable the module.
// They didn't override `posts_per_page`, so the default value will be used
];
// app/Blog/BlogPostListings.php
class BlogPostListings
{
private $perPage;
public function __construct($perPage)
{
$this->perPage = $perPage;
}
public function displayPage()
{
return Blog::paginate($this->perPage);
}
}
// app/Providers/BlogServiceProvider.php
public function register()
{
// If the module isn't enabled, then don't register anything
if (!$this->app->['config']->get('blog.enabled')) {
return false;
}
// Register services, injecting values from the config
$this->app->bind('App\Blog\BlogPostListings', function($app) {
return new \App\Blog\BlogPostListings($app['config']->get('blog.posts_per_page'));
});
// Register the routes required for the blog module to work
$this->app->register('App\Providers\Blog\RouteServiceProvider');
}
I've tried this out, and it works perfectly. The issue I have is when trying to acceptance test the blog module. In the simplest form, I'd like something like the following three tests:
- "Given that the blog modules is enabled and
posts_per_pageis 15, when I got to/blogI see no more than 15 items. - "Given that the blog module is disabled, when I go to
/blogI get a 404.
However, this isn't really possible in either PhpUnit or Behat, but both for the same reason. To demonstrate what I mean:
// tests/Blog/BlogTest.php
public function testISeeTheCorrectNumberOfBlogPosts()
{
// As soon as we enter the test, the BlogServiceProvider has already
// been registered using the default config (which is that the blog
// module is disabled). So setting the config here doesn't make a
// difference, the blog service provider bindings and routes won't
// be registered
config(['blog.enabled' => true]); // Happens after BlogServiceProvider is registered
$this->visit('/blog')->see('Blog'); // Fails, because blog routes were never registered
}
Given that this isn't possible, I was wondering is something is fundamentally wrong with this architecture? Do you have any thoughts on a different way that I could implement an optional module system, while keeping all module code in the same code base, in a way that can be acceptance tested?
If the architecture is sound, an alternative is to have a set of acceptance tests per client (using their personal config overrides). This would work - however, we'd end up with so many duplicated acceptance tests - if 20 clients have the blog module enabled, but all with different posts_per_page, then I'd have to duplicate that test 20 times (once for each client), but modified slightly for their value of posts_per_page. For the remaining clients that have it disabled, I'd need to test that when they visit /blog they get a 404. Although in principle this is a nice idea, the amount of duplication concerns me. I'm also not sure how this option would work if instead of using config files I was instead using a database driven configuration (case 1. at the top of this post).
Any thoughts?
Please or to participate in this conversation.