Page 1 of 1

Testing something which should sleep()?

Posted: Sun Mar 02, 2008 4:39 am
by Chris Corbyn
I'm interested to hear how people would go about this. I need to test a rate throttler for sending emails. As such the class needs to take time() snapshots and sleep() for durations of time based on averages.

I've tested this same thing before and just had my test sleep for anywhere up to 60 seconds which plain sucked :P

This time I've added a bit of bloat for the sake of testing (pseudo code shown):

Code: Select all

interface Sleeper {
  public function sleep($seconds);
}
 
interface Timer {
  public function getTimestamp();
}

Code: Select all

class Throttler implements Sleeper, Timer {
  private $_sleeper;
  private $_timer;
  public function __construct(Sleeper = null, Timer = null) {
    $this->_sleeper = $sleeper;
    $this->_timer = $timer;
  }
 
  public function thingWhichThrottles() {
    $timer = $this->_getTimer();
    if ($someRate > $maxRate) {
      $sleeper = $this->_getSleeper();
      $sleeper->sleep($someSeconds);
    }
  }
 
  public function sleep($seconds) {
    sleep($seconds);
  }
 
  public function getTimestamp() {
    return time();
  }
 
  // ---
 
  private function getSleeper() {
    if (isset($this->_sleeper)) {
      return $this->_sleeper;
    } else {
      return $this;
    }
  }
 
  private function getTimer() {
    if (isset($this->_timer)) {
      return $this->_timer;
    } else {
      return $this;
    }
  }
}
Is this just unneccessary weight? Any better ideas?

PS: I've noticed I use a completely different coding convention on the forum to what I use in my own code :P

EDIT | Completely forgot to explicitly say that obvious I'm mocking Sleeper and Timer in my tests.

Re: Testing something which should sleep()?

Posted: Mon Mar 03, 2008 5:51 pm
by Weirdan
personally I would use either partial mock of object under test or centralized Utils class for a collection of such methods.

Re: Testing something which should sleep()?

Posted: Tue Mar 04, 2008 9:06 pm
by Ambush Commander
As you've done, a mock is the usual way to go about doing things.

I have, however, discovered an interesting way to do this in PHP: using __call() you can create single global PHP function wrapper; instead of sleep() call $php->sleep(). You then have one mock you need to configure.

Re: Testing something which should sleep()?

Posted: Sun Mar 09, 2008 2:12 pm
by Ollie Saunders
I agree with the others, you are going about this the right way. I think your implementation is a little confused, what with return $this and all.

Just test for null-ness in the constructor and assign your default Sleeper and Timer classes to $this->_sleep and $this->_timer respectively.

Re: Testing something which should sleep()?

Posted: Sun Mar 09, 2008 5:06 pm
by Chris Corbyn
ole wrote:Just test for null-ness in the constructor and assign your default Sleeper and Timer classes to $this->_sleep and $this->_timer respectively.
That's how I would have done it previously but people run Swift for very long period of time sometimes and circular references cause all kinds of problems :(

Re: Testing something which should sleep()?

Posted: Sun Mar 09, 2008 5:18 pm
by Ollie Saunders
Interesting.
Where's the circular reference?

Re: Testing something which should sleep()?

Posted: Sun Mar 09, 2008 7:25 pm
by Chris Corbyn
ole wrote:Interesting.
Where's the circular reference?
Maybe I misunderstood you, I assumed you meant to put the check in the constructor:

Code: Select all

class Whatever {
  private $_sleeper;
  private $_timer;
  public function __construct(Sleeper $sleeper = null, Timer $timer = null) {
    $this->_sleeper = is_null($sleeper) ? $this : $sleeper;
    $this->_timer = is_null($timer) ? $this : $timer;
  }
  // .. snip ...
}
In that scenario, the memory used by that object can never be freed by nulling it or unset()'ing it.

I did change my code to this:

Code: Select all

class Thing {
  // .. snip ...
  
   public function sleep($seconds) {
    if (isset($this->_sleeper)) {
      $this->_sleeper->sleep($seconds);
    } else {
      sleep($seconds);
    }
   }
}
It's kind of a conditional proxy.

Re: Testing something which should sleep()?

Posted: Sun Mar 09, 2008 7:42 pm
by Ollie Saunders
Here's how I'd do it.

Code: Select all

<?php
class Sleeper {
    public function sleep($seconds) {
        sleep($seconds);
    }
}
 
class Timer {
    public function time() {
        // whatever your default implementation is
    }
}
 
class Whatever {
    
    private $sleeper;
    private $timer;
    
    public function __construct($sleeper = null, $timer = null) {
        if (!$timer) {
            $timer = new Timer();
        }
        if (!$sleeper) {
            $sleeper = new Sleeper();
        }
        $this->setTimer($timer);
        $this->setSleeper($sleeper);
    }
    
    public function exampleOfSleeperUsage()
    {
        $this->sleeper->sleep(1);
    }
}

Re: Testing something which should sleep()?

Posted: Sun Mar 09, 2008 8:59 pm
by Chris Corbyn
Timer and Sleeper are interfaces ;) My code is mocking them. It's much lighter than making them concrete dependencies IMO. I've avoided such tight coupling on every single line of code in v4 (everything is injected using DI).

I don't think there needs to be separate Timer and Sleeper classes except when testing so a mockable interface should really suffice. I'm open to debate though :)

Re: Testing something which should sleep()?

Posted: Sun Mar 09, 2008 9:27 pm
by Ollie Saunders
I don't think there needs to be separate Timer and Sleeper classes except when testing so a mockable interface should really suffice. I'm open to debate though :)
You say that like what I propose is more complex but mine requires 2 classes and yours requires 2 interfaces. I think mine is more straight-forward and representative of the fact that you want to allow timing and sleeping to be flexible elements of the system (even if it is just for mocks).

The only reason you want to define interfaces is because you like to type hint things. To me what you are doing is adding a big restriction on yourself by type hinting and then having to work around the problems that brings instead of just removing the restriction. I save myself the effort, defining interfaces, by not type hinting.
It's much lighter than making them concrete dependencies IMO.
All I'm doing is shifting a tiny bit of behaviour from a method to a class and reducing some logic in the process. If it develops into a dependency you might want to stick a factory in the middle but I doubt that it will.