5

I'm trying to simulate what Ardent package is doing. Which is validating a model right before saving.

I've created this BaseModel (According to Laravel Testing decoded book). And added this code :

class BaseModel extends Eloquent {
    protected static $rules = [];
    public $errors = [];

    public function validate(){
        $v = Validator::make($this->attributes, static::$rules);

        if($v->passes()) {
            return true;
        }

        $this->errors = $v->messages();

        return false;
    }

    public static function boot(){
        parent::boot();
        static::saving(function($model){
            if($model->validate() === true){
                foreach ($model->attributes as $key => $value) {
                    if(preg_match("/[a-zA-Z]+_confirmation/", $key)){
                        array_splice($model->attributes, array_search($key, array_keys($model->attributes)), 1);
                    }
                }
                echo "test"; //This is for debugging if this event is fired or not
                return true;
            } else {
                return false;
            }
        });
    }
}

Now, this is my Post model :

class Post extends BaseModel {
    public static $rules = array(
            'body' => 'required',
            'user_id' => 'required',
        );

}

In this test i'm expecting it to fail. Instead, it passes ! , $post->save() returns true !

class PostTest extends TestCase {

    public function testSavingPost(){
        $post = new Post();
        $this->assertFalse($post->save());
    }
}

When i tried to throw an echo statement inside the saving event. It didn't appear, So i understand that my defined saving event is not invoked. I don't know why.

rmobis
  • 26,129
  • 8
  • 64
  • 65
Rafael Adel
  • 7,673
  • 25
  • 77
  • 118

2 Answers2

7

check out this discussion: https://github.com/laravel/framework/issues/1181

you'll probably need to re-register your events in your tests.

class PostTest extends TestCase {

    public function setUp()
    {
        parent::setUp();

        // add this to remove all event listeners
        Post::flushEventListeners();
        // reboot the static to reattach listeners
        Post::boot();
    }

    public function testSavingPost(){
        $post = new Post();
        $this->assertFalse($post->save());
    }
}

Or, better yet, you should extract the event registration functionality out of the boot function into a public static method:

  class Post extends Model {

       protected static boot()
       {
           parent::boot();
           static::registerEventListeners();
       }

       protected static registerEventListeners()
       {
           static::saving(...);
           static::creating(...);
           ...etc.
       }

  }

And then call Post::flushEventListeners(); Post::registerEventListeners(); in the setUp() test method.

awei
  • 1,154
  • 10
  • 26
  • Note that [link-only answers](http://meta.stackoverflow.com/tags/link-only-answers/info) are discouraged, SO answers should be the end-point of a search for a solution (vs. yet another stopover of references, which tend to get stale over time). Please consider adding a stand-alone synopsis here, keeping the link as a reference. – kleopatra Apr 07 '14 at 22:51
  • Thanx, i coudn't figure out why this wasn't working :-) – Michiel Aug 07 '14 at 15:12
2

The saving event looks fine for me. The validation fails, so $post->save() returns false. Your test passes because you expect $post->save() to be false (assertFalse), which in this case is correct. Try these tests instead.

public function testSavingInvalidPost() {
    $post = new Post();
    $this->assertFalse($post->save());
}

public function testSavingValidPost() {
    $post = new Post();
    $post->body = 'Content';
    $post->user_id = 1;
    $this->assertTrue($post->save());
}
r15ch13
  • 327
  • 2
  • 14