4
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ Unrelated, but I'm going to have nightmares now... but what hellish framework are you using? An ORM that is built around a singleton DB? ffs, that's just horrible. Oh, and as an asside: each time you call $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\$ Commented Apr 3, 2014 at 19:58
  • \$\begingroup\$ @EliasVanOotegem // Exactly what I want to ask to the previous maintainer. The framework is Kohana. So the actual code saves the result to a property for caching. I tried to make it simple enough to make people understand. \$\endgroup\$
    – Moon
    Commented Apr 4, 2014 at 0:38

1 Answer 1

1
\$\begingroup\$

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.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Well, isn't that an integration test? \$\endgroup\$
    – Moon
    Commented Apr 3, 2014 at 3:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.