I'm using the Selenium2TestCase object to run some tests on a site. I'm trying to modularise these tests for ease of writing and new tests and readability.
With my limited knowledge of Selenium WD and PHPUnit and a distinct lack of useful information available online, I've jumped in and start by splitting my tests up into separate 'page' objects. I don't know whether this is the best solution or if its even technically possible.
This is a simplified version of what I have at the moment...
class Selenium2TestCase_Extension extends PHPUnit_Extensions_Selenium2TestCase
{
public function testPages(){
$pages = array('about', 'contact');
foreach($pages as $page){
// Can I instantiate and run the tests in About_Test from here???
}
}
}
class About_Test extends PHPUnit_Extensions_Selenium2TestCase
{
public function testTitle(){
$this->url('/about');
$this->assertEquals("About Us", $this->title());
}
}
class Home_Test extends PHPUnit_Extensions_Selenium2TestCase
{
public function testTitle(){
$this->url('/home');
$this->assertEquals("Welcome", $this->title());
}
}
I'm not sure if I'm attempting the impossible here or what so any help would be much appreciated!
Yes, you can it with exec or system, or something similar. But it seems that is not proper way.
Is it XY problem?
You could
use groups for launching some of tests.
store those tests in one separate folder and run test over it using command-line interface.
run only wanted tests using command-line interface.
(in your case) use data provider and write one test only.
Related
I'm new to PHPUnit. I have php files that don't have any classes in them. What I came to understand from reading documentation, PHPUnit considers a class as a single unit.
So Does PHPUnit considers a class as a unit?
Is it possible to test php files that don't have any class in them?
Sure, you can absolutely test other PHP scripts.
class MyScriptTest extends PHPUnit_Framework_TestCase {
public function testMyFunction() {
include_once 'path/to/script.php';
$result = someFunction();
$this->assertEquals('expected result', $result);
}
}
So write PHPUnit test classes, and inside a test run whatever code you wish to test and make assertions against it.
Maybe the question comes across as weird, but here is the problem that I'm trying to solve... first of all, please keep in mind I am more of a Java developer getting used to working with PHP, so maybe my thought process is the problem!
I am testing a web site that I have built with Symfony. For my component testing, I create my test class extending WebTestCase and my test I have a set of assertions like the following to verify that the page title is where I want it and containing what I expect:
$text = "Page Title";
$selector = "h2#pageHeading";
$this->assertEquals(1, $crawler->filter($selector)->count(), "Found wrong number of elements using selector ".$selector);
$this->assertEquals(trim($text),
trim($crawler->filter($selector)->text()),
"Element $selector didn't have expected text");
Then I write more tests for other pages within the site, and in all of them I want to test the title is there as it should be, so to be able to reuse code I refactor the above into a function in parent class that the other test classes reuse:
function assertPageTitle($text) {
$selector = "h2#pageHeading";
$this->assertEquals(1, $crawler->filter($selector)->count(), "Found wrong number of elements using selector ".$selector);
$this->assertEquals(trim($text),
trim($crawler->filter($selector)->text()),
"Element $selector didn't have expected text");
}
And I call that method in my tests. As tests develop there are more similar "complex assertions" that I can refactor, and all of them go to the parent class, thus bloating my parent class into a massive assertion container:
protected function assertSelectedOptionContainsTextValue($selector, $text, $value, $crawler) {
...
}
protected function assertMenusContainItems($menus, $crawler) {
...
}
protected function assertErrorMessageShown($message, $crawler) {
...
}
... (more and more) ...
You get the idea. My next thought at this point is to refactor all this "complex assertions" into other classes, probably following the Page Object pattern, but then the other classes won't have access to the assertEquals method unless those other classes also extend WebTestCase or at least PHPUnit_Framework_TestCase, which doesn't seem a very good idea...
So is there an easy way to access the assertEquals method (and related) without having to extend the base PHPUnit classes? Can I use composition somehow?
PHPUnit's built-in assertions are implemented as public static methods in the PHPUnit_Framework_Assert class. Just invoke them as PHPUnit_Framework_Assert::assertTrue(), for instance.
First of all, you are overwriting $text variable at the beginning of your "generic" function - but I understand that you want to only show idea.
Secondly, using unit testing to this kind of tests isn't the best choose. I think you should extract logic of titles to separated classes and then test it by PHPUnit or use more appropriate testing solutions like Behat or Selenium.
If you still haven't changed your opinion... You must extend at least PHPUnit_Framework_TestCase whenever you want to use PHPUnit to testing.
Little hint: Traits would be good to extract your assertions.
In order to create reusable phpunit assertion in trait. That can be used in many tests cases. You can use trait that statically calls PHPUnit_Framework_Assert::assertSame, quick example how to achieve it:
<?php
namespace Tests\Foo;
use PHPUnit_Framework_Assert;
trait AssertTrait
{
public function assertInTraitAlwaysFailing()
{
PHPUnit_Framework_Assert::assertSame(1, 2, 'always failing');
}
}
It would act as helper that can be used in many places where you need same assertion to prevent duplication.
I've been using the Behat english-like test language (Gherkin?) to write test scripts but have quickly come up it's significant limitations.
If I could execute these tests in PHP within the phpunit test scripts that I have set up I could significant expand the tests that I could add. (I'm using FuelPHP).
I've been tinkering around for a few hours trying to get Behat to execute inside a PHPUNIT test script but have not had much luck.
Is this possible?
I think you are confusing something, because what you are saying doesn't make a lot of sense. If you having a hard time expressing the logic with the code, you should ask a specific question on that.
Behat and Mink are both written in PHP, you write your contexts in PHP, there is a gadzillion of plugins to make the life easier (also written in php). As the matter of fact, all your tests are executed in PHP when you run them… Yup!
If you want to compare data from two pages you can simply create a step like this:
/**
* #Then /^the page "(.+)" and the page "(.+)" content should somehow compare$/
*/
public function assertPageContentCompares($page1, $page2)
{
$session = $this->getSession();
$session->visit($page1);
$page1contents = $session->getPage()->getHtml();
$session->visit($page2);
$page2contents = $session->getPage()->getHtml();
// Compare stuff…
}
Besides the obvious, you can use PHPUnit in with Behat / Mink to make the assertions, i.e., in your step definitions. Most (in not all) PHPUnit assertions are static methods, using them is as simple as this:
PHPUnit_Framework_TestCase::assertSame("", "");
You can use Selenium (probably other frameworks too) with PHPUnit, if this is more about unit testing than functional testing, the official documentation tells how.
If you simply hate Gherkin then there's not much you can do with Behat – it's at the core of it. With PhpStorm 8 out there is a pretty good support for it, you can easily navigate around your code and refactor it quickly. If that doesn't cut it, there's another great alternative to Behat called Codeception, where you use pure PHP to define your tests. Maybe that's what you are looking for.
Yes. You can use the library I created: jonathanjfshaw/phpunitbehat.
Your phpunit tests will looks like this:
namespace MyProject\Tests;
use PHPUnit\Framework\TestCase;
use PHPUnitBehat\TestTraits\BehatTestTrait;
class MyTestBase extends TestCase {
use BehatTestTrait;
}
namespace MyProject\Tests;
class MyTest extends MyTestBase {
protected $feature = <<<'FEATURE'
Feature: Demo feature
In order to demonstrate testing a feature in phpUnit
We define a simple feature in the class
Scenario: Success
Given a step that succeeds
Scenario: Failure
When a step fails
Scenario: Undefined
Then there is a step that is undefined
FEATURE;
/**
* #Given a step that succeeds
*/
public function aStepThatSucceeds() {
$this->assertTrue(true);
}
/**
* #When a step fails
*/
public function aStepFails() {
$this->assertTrue(false);
}
}
I wrote a blog post explaining why I think this is not a bad idea.
I have a method with some logic in it and I'm not sure how to unit test it. Because it's a unit test for this specific method it should run without connecting to the database. I read about stubs and mockups but I can't find a way to apply them to this situation.
I would like to force the Client:GetClient to return the client object with the right properties so I can test each logic branch.
class ClientType {
function GetClientType($id) {
$objClient = Client::GetClient($id);
if ($objClient->Returning == 1) {
return 'returning';
}
else {
return 'normal';
}
}
This is the test I had in mind
class ResourceTest extends PHPUnit_Framework_TestCase {
function testGetClientType() {
$objClientType = new ClientType();
$this->assertTrue($objClientType->GetClientType(100), 'normal');
}
}
The problem is the dependency $objClient = Client::GetClient($id); The GetClient will pull a client from database but I need to replace this with a Stub so the unit tests work without real access to the database.
Conclusion
If you have code like the one presented: refactor it and use Dependency Injection.
If you have legacy code or just don't want to refactor try this solution: http://sebastian-bergmann.de/archives/885-Stubbing-Hard-Coded-Dependencies.html
With PHPUnit you can do
$class = $this->getMockClass(
'Client', /* name of class to mock */
array('getClient') /* list of methods to mock */
);
$class::staticExpects($this->any())
->method('getClient')
->will($this->returnValue('foo'));
In general, you want to avoid static methods though:
http://sebastian-bergmann.de/archives/883-Stubbing-and-Mocking-Static-Methods.html
https://kore-nordmann.de/blog/0103_static_considered_harmful.html
EDIT after update
PHPUnit can also stub hardcoded dependencies. See
Stubbing Hard-Coded Dependencies by Sebastian Bergmann
However, since you already noticed by now that it is a Pain the Behind to test statics and hardcoded dependencies, I suggest you remove the hardcoded dependency and static call with a real object that you inject into your ClientType instead.
Another option would be to use http://antecedent.github.io/patchwork (not affiliated with it), which
is a PHP library that makes it possible to redefine user-defined functions and methods
at runtime, loosely replicating the functionality runkit_function_redefine in pure PHP 5.3 code,
which, among other things, enables you to replace static and private methods with test doubles.
Try Mocking for such kind of a problem. I do not know PHP, but we do mocking in Java and C# in such a scenario.
EDIT
I'm sorry I didn't see that you were facing a problem with mocks. Well usually for Mocks you tell them what you expect from them and they respond to the same. LSV principle is being used in mocks. As for PHP, I'm sorry I have no idea on what tool to use for mocks and how you use them
I have a test for testing data model:
<?php
namespace Tests\Model\SQL;
/**
* #group Model_Users
* #group Model
*/
class UsersTest extends TestCase {
/** #var Users */
private $model;
public function setUp() {
parent::setUp();
$this->connection->execute( file_get_contents(
__DIR__ . '/fixtures/countries.sql' ) );
$this->model = new \Users(
$this->connection
);
}
public function testUserExists() {
$exists = $this->model->userExists( 'johndoe' );
$this->assertTrue( $exists );
}
}
The Tests\Model\SQL\TestCase uses SQLite as a database to keep tests as fast as possible.
Anyway, as you may expect, SQLite is not the same database as Postgres, therefore there could be some specialites, which will fail on SQLite and pass on PostgreSQL and vice versa.
My question is:
How to effectively (when keeping DRY in mind) design the architecture to be able to run tests only with SQLite and then only with Postgres.
When I am saying DRY, I mean ideally case, when I write only one testcase for one model and the architecture of my tests will take care of that, so I will be able to run tests like this:
php tests/run.php --group=MockedDbWithSqlite # fast way, prefered, default
php tests/run.php --group=RealDb # slower way, but with more precise results
So judging from the comment the issue is rather independent of any concrete storage issue and more about how one can pass a custom parameter to PHPUnit.
Because, using this parameter, you'd switch out DB drivers.
There are a couple of options to do this but no officially supported "custom parameters" objects or something. Even so it would be nice if someone would add this maybe :)
My suggestion would be to use one of the following to options:
Env variables
Make the test/bootstrap code rely on $_ENV variables.
$dbDriverFactory->getInstanceFor(getenv('DB'));
and using one of the ways below to
export DB=postgress
phpunit
or
DB=postpress; phpunit;
or using phpunits xml config to set the env or whatever you like.
By doing so you could provide two xml configs phpunit-sqlite.xml & phpunit-pg.xml and run:
phpunit -c phpunit-sqlite.xml
It doesn't have to be env. Any of the methods listed in the doc section Setting PHP INI settings, Constants and Global Variables will work.
"Custom" cli parameters
Calling phpunit like this:
phpunit --colors --usePostgres
and adding something like this in test cases or bootstrap code:
if (in_array('--usePostgress', $_SERVER['argv'], true) {
$db = $this->setupPG();
} else {
$db = $this->setupSqlite();
}
I read this differently to edorian. I like to use inheritance for this kind of thing. The advantage is you can run both SQLite and Postgres tests in the same test run. The disadvantage is you have to add some code to each test file where you want to run with both databases. And you have to repeat this for each new database you want to test. Here is how it works:
class UsersTestBase extends TestCase {
//Unchanged
}
class UsersTestSQLite extends UsersTestBase{}
class UsersTestPostgres extends UsersTestBase {
function __construct(){
parent::_construct("postgres");
}
}
So, UsersTest is exactly the same, except the name has changed to append "Base".
You then have a derived UsersTestSQLite class. This does nothing. At this point we've not gained or lost anything.
Then we had UsersTestPostgres. This somehow tells the object to use postgres instead of sqlite. In this case I've assumed TestCase has a constructor that can take the database to use as an optional parameter (with the default being "sqlite"). Obviously there are lots of alternatives to that, but I hope you see the general idea.
Finally you need to add the suitable #group tags to the derived classes, to allow you control over when they are run.