6

I have a class that uses Str::random() which I would like to test.

But when I use Str::shouldReceive('random') in my test, I get a BadMethodCallException saying the method shouldReceive does not exist.

I also tried to mock the class directly and bind it to the IOC but it keeps executing the original class, generating a random string and not the return value I set on the mock.

    $stringHelper = Mockery::mock('Illuminate\Support\Str');
    $this->app->instance('Illuminate\Support\Str', $stringHelper);
    //$this->app->instance('Str', $stringHelper);

    $stringHelper->shouldReceive('random')->once()->andReturn('some password');
    //Str::shouldReceive('random')->once()->andReturn('some password');
dertkw
  • 7,798
  • 5
  • 37
  • 45
toonevdb
  • 329
  • 1
  • 13

4 Answers4

2

What you tried will work as long as you also use IoC to resolve the binding of that class.

$instance = resolve(\Illuminate\Support\Str::class);
$instance::random();
Zedzdead
  • 356
  • 3
  • 18
1

you could not use laravel Mock because Str::random() is not Facade. instead, you can create a new class with a method that set your test environment. like this: ‍‍‍

<?php

namespace App;


use Illuminate\Support\Str;

class Token
{
    static $testToken =null;
    public static function generate(){
        return static::$testToken ?:Str::random(60);
    }

    public static function setTest($string){
        static::$testToken = $string;
    }
}
shayan
  • 15
  • 1
  • 4
0

You won't be able to mock out the Illuminate\Support\Str in this way (see mockery docs. This is how I would test it.

In the class which tries to generate a random string, you can create a method to generate a random string and then override that method in your test (code not actually tested):

    // class under test
    class Foo {
        public function __construct($otherClass) {
            $this->otherClass = $otherClass;
        }

        public function doSomethingWithRandomString() {
            $random = $this->getRandomString();
            $this->otherClass->useRandomString($random);
        }

        protected function getRandomString() {
            return \Illuminate\Support\Str::random();
        }
    }

    // test file
    class FooTest {
        protected function fakeFooWithOtherClass() {
            $fakeOtherClass = Mockery::mock('OtherClass');
            $fakeFoo = new FakeFoo($fakeOtherClass);
            return array($fakeFoo, $fakeOtherClass);
        }

        function test_doSomethingWithRandomString_callsGetRandomString() {
             list($foo) = $this->fakeFooWithOtherClass();
             $foo->doSomethingWithRandomString();
             
             $this->assertEquals(1, $foo->getRandomStringCallCount);
        }

        function test_doSomethingWithRandomString_callsUseRandomStringOnOtherClassWithResultOfGetRandomString() {
            list($foo, $otherClass) = $this->fakeFooWithOtherClass();

            $otherClass->shouldReceive('useRandomString')->once()->with('fake random');
            $foo->doSomethingWithRandomString();
        }
    }
    class FakeFoo extends Foo {
        public $getRandomStringCallCount = 0;
        protected function getRandomString() {
            $this->getRandomStringCallCount ++;
            return 'fake random';
        }
    }
hms5232
  • 323
  • 1
  • 6
awei
  • 1,154
  • 10
  • 26
0

Accroding to Mockery docs, if you want to mock public static method, you should use alias.

Therefore, you can:

Mockery::mock('alias:Illuminate\Support\Str')->shouldReceive('random')->once()->andReturn('some password');
hms5232
  • 323
  • 1
  • 6