PHPUnit with Selenium2 - share session not working - unit-testing

I'm doing some tests with PHPUnit and Selenium and i would like all of them to run in the same browser window.
I've tried starting the Selenium Server with
java -jar c:\php\selenium-server-standalone-2.33.0.jar -browserSessionReuse
but with no visible change.
I've also tried with shareSession() in the setup
public function setUp()
{
$this->setHost('localhost');
$this->setPort(4444);
$this->setBrowser('firefox');
$this->shareSession(true);
$this->setBrowserUrl('http://localhost/project');
}
but the only change is that it opens a window for every test, and not really sharing the session. I'm out of ideas at this point.
My tests look like this:
public function testHasLoginForm()
{
$this->url('');
$email = $this->byName('email');
$password = $this->byName('password');
$this->assertEquals('', $email->value());
$this->assertEquals('', $password->value());
}

Here's the elegant solution. To share browser sessions in Selenium2TestCase, you must set sessionStrategy => 'shared' in your initial browser setup:
public static $browsers = array(
array(
'...
'browserName' => 'iexplorer',
'sessionStrategy' => 'shared',
...
)
);
The alternative (default) is 'isolated'.

You do not need to use the flag -browserSessionReuse
In your case The set up function running before every test and starting new instance.
This is what i did to prevent this to happen (Its little bit ugly but work for me both in Windows and Ubuntu):
I created helper class with static ver: $first and initialized it.
helper.php:
<?php
class helper
{
public static $first;
}
helper::$first = 0;
?>
Edit main test file setUp() function(and add require_once to helper.php):
require_once "helper.php";
class mySeleniumTest extends PHPUnit_Extensions_SeleniumTestCase
{
public function setUp()
{
$this->setHost('localhost');
$this->setPort(4444);
if (helper::$first == 0 )
{
$this->shareSession(TRUE);
$this->setBrowser('firefox');
$this->setBrowserUrl('http://localhost/project');
helper::$first = 1 ;
}
}
....
setHost and setPort outside the if because the values restarted after each test(For me...) and need to set up every time (if the selenium server is not localhost:4444)

Just found an (much) faster way to proceed : If you perform several test in one function, all test are performed in the same window. The setback is that the tests and reporting won't be nicely presented by tests, but the speed is way up!
In the same function for each test just use:
$this->url('...');
Or
$this->back();

Related

Issue testing Laravel Controller with Mockery | trying to get property of non-object

I'm very new to testing controllers and I'm running into a problem with a method(). I believe I'm either missing something in my test or my Controller / Repository is designed incorrectly.
The application I'm writing is basically one of those secure "one time" tools. Where you create a note, the system provides you with a URL, once that url is retrieved the note is deleted. I actually have the application written but I am going back to write tests for practice (I know that's backwards).
My Controller:
use OneTimeNote\Repositories\NoteRepositoryInterface as Note;
class NoteController extends \Controller {
protected $note;
public function __construct(Note $note)
{
$this->note = $note;
}
public function getNote($url_id, $key)
{
$note = $this->note->find($url_id, $key);
if (!$note) {
return \Response::json(array('message' => 'Note not found'), 404);
}
$this->note->delete($note->id);
return \Response::json($note);
}
...
I've injected my Note interface in to my controller and all is well.
My Test
use \Mockery as M;
class OneTimeNoteTest extends TestCase {
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('OneTimeNote\Repositories\EloquentNoteRepository');
}
public function mock($class)
{
$mock = M::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testShouldReturnNoteObj()
{
// Should Return Note
$this->mock->shouldReceive('find')->once()->andReturn('test');
$note = $this->call('GET', '/note/1234567890abcdefg/1234567890abcdefg');
$this->assertEquals('test', $note->getContent());
}
}
...
The error I'm getting
1) OneTimeNoteTest::testShouldReturnNoteObj
ErrorException: Trying to get property of non-object
/Users/andrew/laravel/app/OneTimeNote/Controllers/NoteController.php:24
Line 24 is in reference to this line found in my controller:
$this->note->delete($note->id);
Basically my abstracted repository method delete() obviously can't find $note->id because it really doesn't exist in the testing environment. Should I create a Note within the test and try to actually deleting it? Or would that be something that should be a model test? As you can see I need help, thanks!
----- Update -----
I tried to stub the repository to return a Note object as Dave Marshall mentioned in his answer, however I'm now receiving another error.
1) OneTimeNoteTest::testShouldReturnNoteObj
BadMethodCallException: Method Mockery_0_OneTimeNote_Repositories_EloquentNoteRepository::delete() does not exist on this mock object
I do have a delete() method in my repository and I know it's working when I test my route in the browser.
public function delete($id)
{
Note::find($id)->delete();
}
You are stubbing the note repository to return a string, PHP is then trying to retrieve the id attribute of a string, hence the error.
You should stub the repository to return a Note object, something like:
$this->mock->shouldReceive('find')->once()->andReturn(new Note());
Building upon Dave's answer, I was able to figure out what my problem is. I wasn't mocking the delete() method. I didn't understand the need to mock each individual method in my controller that would be called.
I just added this line:
$mock->shouldReceive('delete')->once()->andReturnNull();
Since my delete method is just deleting the note after it is found, I went ahead and mocked it but set it to return null.

Laravel eloquent model reset booted property during unit tests

I have a boot method on a class that extends from Eloquent's base model class. I want to run some unit tests and I need it to fire on each test. Unfortunately, the class ID index is persisting for the Illuminate\Database\Eloquent\Model::$booted property which is set in the constructor that I extend from - so it only works for the first test. I've tried adjusting a few phpunit's flags - I tried process isolation - nothing seems to work.
Is there a way I can reset this property so each test can fire my boot method?
Illuminate\Database\Eloquent\Model.php
public function __construct(array $attributes = array())
{
if ( ! isset(static::$booted[get_class($this)])) <-- Keeps persisting
{
static::$booted[get_class($this)] = true;
static::boot();
}
...
}
app\models\Foo.php
class Foo extends Model {
...
protected static function boot() { <-- first test to execute wins, all other calls get skipped
...
}
...
}
ahh - just a tad more research and the answer is revealed!
My question is basically a duplicate of this question Laravel 4 Model Events don't work with PHPUnit, which references this thread on github for the solution

How do I explicitly specify where a Nancy view is located?

First of all I know what the problem is, I just don't know Nancy well enough to know how to fix it.
I have a unit test failing when as part of the appharbor build process. The same test also fails when NCrunch executes it. But, when executed by VS2012 it works fine.
The test looks like this:
[Test]
public void Get_Root_Should_Return_Status_OK()
{
// Given
var browser = new Browser(new Bootstrapper());
// When
var result = browser.Get("/");
// Then
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
}
HomeModule part handling the "/" route looks like this:
Get["/"] = _ => View["home.sshtml"];
home.sshtml is in the Views folder.
If I replace the above with:
Get["/"] = _ => "Hello World!;
Then the test goes green.
So plainly the problem is that when running the test in NCrunch and appharbor the home.sshtml file cannot be found.
How do I explicitly tell Nancy where the file is?
PS The view file is being copied to the output directory.
PPS I have also tried explicitly telling Nancy where the Views are like and that doesn't work either.
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
var directoryInfo = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
if (directoryInfo != null)
Environment.CurrentDirectory = directoryInfo.FullName;
Conventions.ViewLocationConventions.Add((viewName, model, viewLocationContext) => String.Concat("Views/", viewName));
}
The problem is due to the fact that NCrunch doesn't copy the views to the output directory when it compiles and copies the bin directory for running the tests.
What you need to do is set the views to Copy Always, and then in your unit testing project add a IRootPathProvider implementation:
public class StaticPathProvider : IRootPathProvider
{
public static string Path { get; set; }
public string GetRootPath()
{
return Path;
}
}
(Not entirely sure on the path, I can't remember, think it's just where the executing assembly is)
And register that in your bootstrapper for unit tests.
var browserParser = new Browser(with =>
{
...
with.RootPathProvider<StaticPathProvider>();
...
});
Downside is when deploying you need to delete the view directory from your /bin directory.
The alternative is to do what you've already done, embed your views.

How to mock a CakePHP behavior for unit testing

I've just started with unit testing in CakePHP (yay!) and ran into the following challenge. Hope somebody can help me :-)
Situation
My model uses a Behavior to send changes to an API after saving it locally. I would like to fake all calls made to the API during the test (those will be tested seperately) to save load on the API server, and more important, not actually save the changes :-)
I'm using CakePHP 2.4.1.
What I've tried
Read the docs. The manual shows how to do this for Components and Helpers but not for Behaviors.
Google. What I've found:
A Google Group post which says it simply "isn't possible". I don't take no for an answer.
An article explaining how to mock an object. Comes pretty close.
The code from the article reads:
$provider = $this->getMock('OurProvider', array('getInfo'));
$provider->expects($this->any())
->method('getInfo')
->will($this->returnValue('200'));
It might be the wrong direction, but I think that might be a good start.
What I want
Effectively: A snippet of code to demo how to mock a behavior in a CakePHP Model for unit testing purposes.
Maybe this question will result in an addition of the CakePHP manual too as an added bonus, since I feel it's missing in there.
Thanks in advance for the effort!
Update (2013-11-07)
I've found this related question, which should answer this question (partly). No need to mock up the API, instead I can create a Behavior test that the model will use.
I'm trying to figure out what that BehaviorTest should look like.
Use the class registry
As with many classes, behaviors are added to the class registry using the class name as the key, and for subsequent requests for the same object loaded from the classregistry. Therefore, the way to mock a behavior is simply to put it in the class registry before using it.
Full Example:
<?php
App::uses('AppModel', 'Model');
class Example extends AppModel {
}
class TestBehavior extends ModelBehavior {
public function foo() {
throw new \Exception('Real method called');
}
}
class BehaviorExampleTest extends CakeTestCase {
/**
* testNormalBehavior
*
* #expectedException Exception
* #expectedExceptionMessage Real method called
* #return void
*/
public function testNormalBehavior() {
$model = ClassRegistry::init('Example');
$model->Behaviors->attach('Test');
$this->assertInstanceOf('TestBehavior', $model->Behaviors->Test);
$this->assertSame('TestBehavior', get_class($model->Behaviors->Test));
$this->assertSame(['foo' => ['Test', 'foo']], $model->Behaviors->methods());
$model->foo();
}
public function testMockedBehavior() {
$mockedBehavior = $this->getMock('TestBehavior', ['foo', 'bar']);
ClassRegistry::addObject('TestBehavior', $mockedBehavior);
$model = ClassRegistry::init('Example');
$model->Behaviors->attach('Test');
$this->assertInstanceOf('TestBehavior', $model->Behaviors->Test);
$this->assertNotSame('TestBehavior', get_class($model->Behaviors->Test));
$expected = [
'foo' => ['Test', 'foo'],
'bar' => ['Test', 'bar'],
'expects' => ['Test', 'expects'], // noise, due to being a mock
'staticExpects' => ['Test', 'staticExpects'], // noise, due to being a mock
];
$this->assertSame($expected, $model->Behaviors->methods());
$model->foo(); // no exception thrown
$mockedBehavior
->expects($this->once())
->method('bar')
->will($this->returnValue('something special'));
$return = $model->bar();
$this->assertSame('something special', $return);
}
}

Unit-testing scripts in CakePHP application

I have developed a deal application using CakePHP. Now I want to write the unit-scripts using PHPUnit. I have installed PHPUnit on my server and test core tests is working fine. Installed Xdebug also for code analyze. When I am going to scripts for existing application then it is not working. I am able to write the unit-script for login menthod in model. But can't write the scripts for remaining methods.
<?php
App::uses('User', 'Model');
class UserTest extends CakeTestCase {
public $fixtures = array('app.user');
public $dropTables = false;
public function setUp() {
parent::setUp();
$this->User = ClassRegistry::init('User');
}
public function testLogin() {
$result = $this->User->find('count', array(
'conditions' => array(
// making some assumptions about the test data here
'email' => 'test.user#gmail.com',
'password' => 'f1054da373ace628dc73b8ec52eb28072b074940',
),));
$expected = 1;
$this->assertEquals($expected, $result);
}
}
?>
it is working well. But I am not able to write scripts for the remaining methods.
Try using bake and bake the tests to get started with the correct testing structure:
cake bake test
This might help push you in the write direction with the way Cake expects the test to be structured. It will also create the empty methods to test all of the methods in the controller.
Can you be more specific about what is not working?