Asked  7 Months ago    Answers:  5   Viewed   37 times

So I am not sure exactly what I would have to show you guys, how ever if you need more code please do not hesitate to ask:

So this method will set up the initMailer for Zend with in our application:

protected function _initMailer()
{
    if ('testing' !==  APPLICATION_ENV) {
        $this->bootstrap('Config');
        $options = $this->getOptions();
        $mail = new Zend_Application_Resource_Mail($options['mail']);
    }elseif ('testing'  ===  APPLICATION_ENV) {
        //change the mail transport only if dev or test
        if (APPLICATION_ENV <> 'production') {

            $callback = function()
            {
                return 'ZendMail_' . microtime(true) .'.tmp';
            };

            $mail = new Zend_Mail_Transport_File(
                array('path' => '/tmp/mail/',
                        'callback'=>$callback
                )
            );

            Zend_Mail::setDefaultTransport($mail);
        }
    }


    return $mail;
}

You can see the closure that lies with in. When I run any tests that use this code I get:

Exception: Serialization of 'Closure' is not allowed 

and thus all the tests in relation to this "closure" fails. So I am here asking you guys what I should do.

For clarification on the above, all were doing is saying that any email we send out we want to store information about that email in a folder in the /tmp/mail/ directory in a file.

 Answers

88

Apparently anonymous functions cannot be serialized.

Example

$function = function () {
    return "ABC";
};
serialize($function); // would throw error

From your code you are using Closure:

$callback = function () // <---------------------- Issue
{
    return 'ZendMail_' . microtime(true) . '.tmp';
};

Solution 1 : Replace with a normal function

Example

function emailCallback() {
    return 'ZendMail_' . microtime(true) . '.tmp';
}
$callback = "emailCallback" ;

Solution 2 : Indirect method call by array variable

If you look at http://docs.mnkras.com/libraries_23rdparty_2_zend_2_mail_2_transport_2file_8php_source.html

   public function __construct($options = null)
   63     {
   64         if ($options instanceof Zend_Config) {
   65             $options = $options->toArray();
   66         } elseif (!is_array($options)) {
   67             $options = array();
   68         }
   69 
   70         // Making sure we have some defaults to work with
   71         if (!isset($options['path'])) {
   72             $options['path'] = sys_get_temp_dir();
   73         }
   74         if (!isset($options['callback'])) {
   75             $options['callback'] = array($this, 'defaultCallback'); <- here
   76         }
   77 
   78         $this->setOptions($options);
   79     }

You can use the same approach to send the callback

$callback = array($this,"aMethodInYourClass");
Wednesday, March 31, 2021
 
qitch
answered 7 Months ago
49

Your problem is that you aren't injecting your dependency (the closure), which always makes unit testing harder, and can make isolation impossible.

Inject the closure into SUT::foo() instead of creating it inside there and you'll find testing much easier.

Here is how I would design the method (bearing in mind that I know nothing about your real code, so this may or may not be practical for you):

class SUT 
{
    public function foo($bar, $someFunction) 
    {
        $bar->baz($someFunction);
    }
}

class SUTTest extends PHPUnit_Framework_TestCase 
{
    public function testFoo() 
    {
        $someFunction = function() {};

        $mockBar = $this->getMockBuilder('Bar')
             ->setMethods(array('baz'))
             ->getMock();
        $mockBar->expects($this->once())
             ->method('baz')
             ->with($someFunction);

        $sut = new SUT();

        $sut->foo($mockBar, $someFunction);
    }
}
Wednesday, March 31, 2021
 
mgraph
answered 7 Months ago
25

You have a typo in your controller: ->withErros($validator) - it should be withErrors. The difference is big.

Laravel converts withErros($validator) into with(['erros' => $validator]), which means trying to put the entire validator object into the session. To put things into the session storage, it needs to be serialized first, which is what is causing the error.

Wednesday, March 31, 2021
 
Maury
answered 7 Months ago
29

Even with setExpectedException, your test is still regular PHP code, and follows PHP's normal rules. If an exception is thrown, program flow immediately jumps out of the current context until it reaches a try/catch block.

In PHPUnit, when you use setExpectedException, it tells PHPUnit's core that when it should be expecting an exception from the code that's about to run. It therefore waits for it with a try/catch block and passes the test if the catch is called with the type of exception it is expecting.

However, within your test method, the normal PHP rules still apply -- when the exception happens, that's the end of the current code block. Nothing more in that method will be executed, unless you have your own try/catch block within the test method.

So therefore, in order to test multiple exceptions, you have a few options:

  1. Add your own try/catch to the test method, so that you can carry on with further tests within that method after the first exception.

  2. Split the tests into separate methods, so that each exception is in its own test.

  3. This particular example looks like a good case to use PHPUnit's dataProvider mechanism, because you're basically testing the same functionality with two sets of data. The dataProvider feature allows you to define a separate function that contains an array of input data for each set of values you want to test. These values are then passed one set at a time into the test method. Your code would look something like this:

    /**
     * @dataProvider providerConfigOverriding
     */
    public function testConfigOverriding($filename, $expectedExceptionText) {
        $this->dependencyContainer = new DependencyContainer(__DIR__ . "/../../Resources/valid_json.json");
        $this->assertEquals('overriden', $this->dependencyContainer->getConfig('shell_commander')['pygmentize_command']);
    
        $this->setExpectedException('Exception', $expectedExceptionText);
        $this->dependencyContainer = new DependencyContainer($filename);
    }
    
    public function providerConfigOverriding() {
        return array(
            array('unexisting_file', 'Configuration file at path "unexisting_file" doesn't exist.'),
            array(__DIR__ . "/../../Resources/invalid_json.json", "Configuration JSON file provided is not valid."),
        );
    }
    

Hope that helps.

Saturday, May 29, 2021
 
Sanguine
answered 5 Months ago
90

If you are using queues you cannot serialize Objects that contains closures, it's the way PHP works. Every time you push a job onto a queue, Laravel serializes its properties to a string that can be written to the database, but anonymous functions (e.g. functions that do not belong to any class) cannot be represented as a string value, thus they cannot be serialized. So basically when you push your RequestMail job onto the queue, Laravel tries to serialize its properties, but $request is an object that contains closures, so it cannot be serialized. To solve the problem you have to store in the RequestMail class only properties that are serializable:

protected $phone;

/**
 * Create a new message instance.
 *
 * @return void
 */
public function __construct(Request $request)
{
    $this->phone = $request->get('phone');
}

public function build()
{
    return $this->from('robot@domain.com')
                ->view('request')
                ->subject('New request for exchange')
                ->with(['phone' => $this->phone]);
}

Doing such thing you are keeping only the properties of $request that you actually need, in this case the phone number, that is a string and it is perfectly serializable.

EDIT I've realized just now that this is a duplicate

EDIT 2 i've edited the code with the correct request parameter retrieval for further reference.

Saturday, May 29, 2021
 
Crontab
answered 5 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :