I just jumped to a new project, which uses the active record pattern. It's a subscription service website.
I have the following User object that extends a framework specific ORM object:
class User extends ORM {
public function isSubscriber()
{
$activeSubscription = $this->subscriptions->where('active','=',1)->find_all();
return $activeSubscription->count();
}
}
In client code, I have the following code:
class Test_Controller extends Controller {
public function test()
{
// Framework speific way to get a current user
// returns User orm
$user = Auth::instance()->get_user();
var_dump($user->isSubscriber());
}
}
As you see, it's very straightforward and easy to understand. What's bothering me is that there's no way to test this simple User object unless I hack it.
The problem is that inside ORM objects, it uses a singleton pattern (something like Database::getInstance()) to get a database object and uses it to query to database, making it impossible to properly mock it.
I extracted isSubscriber() from User to a service like this:
class SubscriberVerificationService {
public function __construct(SubscriptionRepositoryInterface $subscriptionRepository)
{
$this->subscriptionRepository = $subscriptionRepository;
}
public function isSubscriber(User $user)
{
$subscriptions = $this->subscriptionRepository->findActiveSubscriptionsByUserId($user->id);
return count($subscriptions);
}
}
Client code
class Test_Controller {
public function __construct(SubscriberVerificationService $subscriberVerificationService)
{
$this->subscriberVerificationService = $subscriberVerificationService;
}
public function test()
{
$user = Auth::instance()->get_user();
var_dump($this->subscriberVerificationService->isSubscriber($user));
}
}
Now I can properly mock SubscriptionRepositoryInterface and and User objects in my test.
I feel like this is the way to go, but I just want to hear advice from people who use the active record pattern day-to-day.
It doesn't have to be a repository (SubscriptionRepositoryInterface) in the service. It can be a Subscription ORM as long as I can mock it.
1 Answer 1
Avoid hacking the code, if possible. Instead, you should have a test environment with a testing database. Database::getInstance() should use a configuration file containing the database connection parameters. It should configure itself to use the testing database when running unit tests.
-
2\$\begingroup\$ Well, isn't that an integration test? \$\endgroup\$Moon– Moon2014年04月03日 03:37:23 +00:00Commented Apr 3, 2014 at 3:37
$user->isSubscriber(), do you really want to perform that query? I mean: why not assign it to a property and return that? It's what most ORM's would do, after all \$\endgroup\$