Testing something which should sleep()?

Discussion of testing theory and practice, including methodologies (such as TDD, BDD, DDD, Agile, XP) and software - anything to do with testing goes here. (Formerly "The Testing Side of Development")

Moderator: General Moderators

Post Reply
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Testing something which should sleep()?

Post 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.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Re: Testing something which should sleep()?

Post by Weirdan »

personally I would use either partial mock of object under test or centralized Utils class for a collection of such methods.
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Re: Testing something which should sleep()?

Post 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.
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Re: Testing something which should sleep()?

Post 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.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Testing something which should sleep()?

Post 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 :(
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Re: Testing something which should sleep()?

Post by Ollie Saunders »

Interesting.
Where's the circular reference?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Testing something which should sleep()?

Post 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.
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Re: Testing something which should sleep()?

Post 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);
    }
}
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Testing something which should sleep()?

Post 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 :)
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Re: Testing something which should sleep()?

Post 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.
Post Reply