Archive for the ‘symfony’ Category

Setting up a symfony project with PHPUnit on Hudson

Wednesday, October 6th, 2010 by Franz Stelzer

We are using Hudson now for several months as Continous Integration System. This short article describes how we have configured a hudson project for a symfony 1.4 project. I am assuming that the reader is already used to Hudson and knows how a normal project has to be configured.

PHPUnit is the test framework of our choice (surprise, surprise) and we are using the sfPHPUnit2Plugin for all our projects. If you do not now this plugin you may first read another post where the usage and features are described in detail.

All requirements in short:

  • Hudson has to be installed
  • Hudson plugin xUnit Plugin has to be installed
  • the symfony project needs the sfPHPUnit2Plugin
  • PHPUnit has to be installed on your test server

Ok, here the configuration steps of the hudson project:

1. Configure your project

Configure standard settings for a hudson project like source-code management settings or email notifications. Please check the official docs if you do not know how to handle this.

2.  Add a shell build step

Building a symfony project in a test environment is pretty easy. With the help of some shell commands the project is completely configured and ready for testing. Those shell commands may be entered in the build step section of the hudson project. Defining the correct commands is the main part during the configuration process.

Our configuration looks like this:

1
2
3
4
5
6
cd $WORKSPACE/trunk
sh _deployment/install_test.sh
php symfony cc
php symfony phpunit:test-all --configuration --options="--log-junit=build/testresult_$BUILD_NUMBER.xml"
cd build
ln -s -f testresult_$BUILD_NUMBER.xml currentTestResult.xml
  1. Jump in the project root of the project
  2. Install the project on the test server with the help of a internal shell script. This step includes for example the generation of the databases.yml.
  3. Clear the symfony cache (always a good choice)
  4. Run all PHPUnit tests including unit and functional tests. The test result is written in a jUnit compatible logfile (needed for the xUnit Plugin).
  5. Jump in the build directory, which is internally used by Hudson
  6. Symlink the latest testresult

3. Configure Post-Build-Action

After the xUnit Plugin is installed correctly, an additional PHPUnit Pattern field should be displayed in the post build action section. In this field has to be entered:

1
trunk/build/currentTestResult.xml

The options “Fail the build if test results were not updated this run” and “Delete temporary JUnit file” should be both checked.

The xUnit Plugin takes the currentTestResult.xml file, which was previously created with the help of the sfPHPUnit2Plugin and analyzes it. When everything works fine, you should be able to review the created test reports.

Here some screenshots how this result could look like.

Build history:

Build history

Trend graph of the test results:

Trend graph

Functional test on uploads for a web service – a workaround

Wednesday, August 4th, 2010 by Franz Stelzer

Yesterday i was faced with a problem on implementing a functional test for a web service, which handles a file upload.
However this workflow seemed to be pretty easy so, as it is documented in the Jobeet Tutorial:

1
2
3
4
5
6
7
8
9
10
11
12
$browser->get('/job/new')->
with('request')->begin()->
isParameter('module', 'job')->
isParameter('action', 'new')->
end()->

click('Preview your job', array('job' => array(
'company' => 'Sensio Labs',
'url' => 'http://www.sensio.com/',
'logo' => sfConfig::get('sf_upload_dir').'/jobs/sensio-labs.gif',
// other parameters ...
));

First the form is called and rendered from the job/new page and than the test browser clicks on the submit button. The call of the click method is the most problematic point of this issue. This click method does some magic and assigns the given file from an absolute path to the request. This magic will not be done in a normal post request. Trying to submit a file directly with the post action during a functional test will end up to empty file values in the action code.

This will not work, also our web service would require it:

1
$browser->post('/job/new', array('job' => array('company' => 'Sensio Labs', 'logo' => sfConfig::get('sf_upload_dir').'/jobs/sensio-labs.gif',') ))->....

Now we have a bad situation: The web service requires the post request, but the functional test does only work with the click method.

Searching a while through the web exposed that i am not the only one with this problem. Similar topics or questions could be read in the forum, on stackoverflow or on the user mailing list.

The workaround for this problem is as easy as ugly: We have to render a form for this functional test but have to avoid that this rendered form is accessible in the producation environment.

The action code of route test/upload looks like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public function executeUpload(sfWebRequest $request)
{
  $this->form = new TestUploadForm();
  if($request->isMethod('post'))
  {
    $this->form->bind($request->getParameter('upload'), $request->getFiles($this->form->getName()));
 
    if($this->form->isValid())
    {
      // make something with the form ...
      $values = $this->form->getValues();
      $file = $values['file'];
      $filepath = sfConfig::get('sf_data_dir').'/test/some.thing';
      $file->save($filepath);
    }
    else
    {
      // make some error handling ...
    }
  }
  else
  {
    // only render form in dev or test environment
    if(in_array(sfConfig::get('sf_environment'), array('dev', 'test')))
    {
      throw new sfException('not enabled for this environment');
    }
  }
}

For better understanding, this action saves the incoming file always to the same location.

The template for the faked form rendering looks like (standard template for rendering a form)

1
2
3
4
5
6
7
8
9
10
<?php echo form_tag($some_route, array('multipart' => true)); ?>
  <table>
    <?php echo $form; ?>
      <tr>
        <td colspan="2">
          <input type="submit" value="GO" />
        </td>
      </tr>
    </table>
</form>

After calling test/upload in your browser, you should see this rendered form now. This form is rendered in the test environment, too.
The functional test is now able to handle this form as it would be placed on a normal website.

The functional test code could look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$generatedFile = sfConfig::get( 'sf_data_dir' ).'/test/some.thing';
 
// cleanup
@unlink($generatedFile);
 
// do the fake request
$browser->
  get('/test/upload')->
    with('request')->begin()->
      isParameter('module', 'test')->
      isParameter('action', 'upload')->
    end()->
 
// do the real web service request
  click('GO', array( 'upload' => array( 'name' => 'foo' , 'file' => $testfile)))->
    with('request')->begin()->
      isParameter('module', 'test')->
      isParameter('action', 'upload')->
    end()->
 
    with('response')->begin()->
      isStatusCode(200)->
    end();
 
// action should have saved the incoming file to the expected location
$this->assertTrue(file_exists($generatedFile));

This works now like the example from the Jobeet Tutorial. First we make our workaround and fake a rendered form in the get request. Afterwards we make the post request within the click call.
When everything went fine, the incoming file should be copied to the expected file location.

I am not proud of this workaround, yet it is the only simple solution doing this trick without hacking the symfony core code.
If you have any other ideas or better solutions, please let me know!

sfPHPUnit2Plugin version 0.9.0 is out

Thursday, June 10th, 2010 by Franz Stelzer

The sfPHPUnit2Plugin is now available in version 0.9.0. The new release is the reason of some feedback i got within my older blog post or by mail and i hope it contains all the requested things.

Two changes/enhancements have to be pointed out:
The older release had some trouble with symfony 1.2 projects and so a compatibility task was added lately.
The biggest new feature is the support for selenium tests. This support was established through a great contribution of Richard Shank. Thanks Richard!

And here the complete changelog

  • added compatibility task for symfony 1.2
  • added selenium support (Special Thanks to Richard Shank!)
  • added experimental support for plugin tests
  • added possibility to customize skeleton template files
  • adepted changes to latest changes in the lime_test class

The next big thing will be the support of plugin tests. The sfTaskExtraPlugin provides plugin tests for lime. I will have a deeper look into this mechanism and try to migrate it to this plugin.