How to propagate setUp and tearDown across all tests in phpUnit? - phpunit

I have the following tests/ folder:
tests/
ClassOne.test.php
ClassTwo.test.php
ClassThree.test.php
I am having to copy the following setUp() and tearDown() methods into each of these files:
public function setUp()
{
$this->dbTestData();
}
public function tearDown()
{
$this->dbClear();
}
private function dbTestData() {
// populate the database with a few entries
Entities\AutocompleteValue::create(array('field_name' => 'testing1', 'entry_value' => 'Aisforapple'));
Entities\AutocompleteValue::create(array('field_name' => 'testing2', 'entry_value' => 'Bisforball'));
Entities\AutocompleteValue::create(array('field_name' => 'testing3', 'entry_value' => 'Cisforcat'));
Entities\AutocompleteValue::create(array('field_name' => 'testing4', 'entry_value' => 'Disfordog'));
}
private function dbClear() {
DB::table('autocomplete_values')->delete();
}
I have considered writing a single separate class which contains these methods, require() that file in each of the test files, and extend from that class instead of PHPUnit_Framework_Testcase. Is there an easier solution to this?
I do not have easy access to phpunit.xml, because my coding framework's CLI tool (Laravel, artisan) handles its creation. Hence, it would be better if there is a solution not involving that file.

Give in and create the base class. That is the standard solution in this situation. It brings other advantages, such as a good place to keep the static assert extensions you create.
I'm struggling to even come up with an alternative approach, let alone one that is easier. All I can suggest is to use a script that scans each test file and inserts setUp, tearDown and their dependencies if they are not found. But (IMHO) that is a much more complex solution for no significant benefit.

Related

PHPUnit: how to use assertions outside of the "test..." methods?

I have the following code:
private function registerShutdownFunction(): void
{
register_shutdown_function(function () {
$this->dropDatabasesAndUsersIfExist();
});
}
And this code:
private function dropDatabasesAndUsersIfExist(): void
{
// some code for deletion of the databases...
foreach ($connections as $connection) {
$this->assertNotContains($connection, $databases);
}
}
But dropDatabasesAndUsersIfExist is not a "test..." method. And phpunit ignores assertions outside of the test methods.
And seems there are problems may occur, because this shutdown function running directly before the die of the script...
You can use PHPUnit's Assert class outside of test cases if that is really what you want to do:
PHPUnit\Framework\Assert::assertNotContains($connection, $databases);
Edit: After reading your question one more time I'm not really sure if my answer helps you. If I got you right, you are already using the assertion but it did not behave as you'd expect it. My guess is that you want the whole test run to fail if any of the assertions in dropDatabasesAndUsersIfExist was not met.
One solution could be to move the checks you are doing in dropDatabasesAndUsersIfExist to a separate test class that should be executed last. You can achieve this by appending another test suite with the new class right after your test suite(s).

Mockery false positive in Laravel controller test

I'm trying to learn how to use Mockery with Laravel 5. I've based my efforts mostly on Way's book (Laravel Testing Decoded) and other tutorials, which say integration [with PHPUnit] only requires the tearDown() method. So I've included that. The problem is that it doesn't seem to be resetting things between tests. My test class contents look essentially like this:
public function __construct()
{
$this->mock = Mockery::mock('Class\To\Mock');
}
public function tearDown()
{
Mockery::close();
}
public function test_RedirectWithoutAuthentication()
{
// Act
$this->call('GET', '/path/1');
// Assert
$this->assertRedirectedTo('/auth/login');
}
public function test_X()
{
// Arrange
$this->mock->shouldReceive('MockedClassMethod')->once();
// Act
$this->call('GET', '/path/1');
}
The first test works and the Auth middleware kicks the user to the login page. In the interest of TDD, I've written the second test before the MockedClassMethod is actually written. So to my way of thinking, it should fail spectacularly. But it doesn't. It passes!
If I change the order of the tests, it "works" (unwritten fails, auth passes) which leads me to believe that it's not really an order problem, but something to do with one test not getting cleaned up before the next.
Any insights will save my remaining hair from being pulled out. :-)
In accordion with the PHPUnit doc:
The setUp() and tearDown() template methods are run once for each test
method (and on fresh instances) of the test case class.
The constructor is called only the first time so the tearDown is called before the second test executed of the class and the effect is that the mockery instance was closed.
In order to solve your problem you can use the setup method for init the mocked object. So replace the class constructor of the test class with the setup method as follow:
Try to use this:
protected function setUp()
{
$this->mock = Mockery::mock('Class\To\Mock');
}
instead of:
public function __construct()
{
$this->mock = Mockery::mock('Class\To\Mock');
}
Hope this help
I had tried using the setUp() method as described by Matteo, and it caused the tests not to run at all. So I abandoned that course thinking I was way off. But Matteo's suggestion turned me back to it.
A little digging into why it caused the tests to fail showed that the $app object was never getting created. Hmm... So then it dawned on me that the setUp() method was overriding some important stuff. So to fix it, all that was needed was to call the parent method first! Like so:
public function setUp()
{
parent::setUp();
$this->mock = Mockery::mock('Class\To\Mock');
}
From that point, the setUp() and tearDown() worked as expected.
I'm still not sure why all the examples I seem to find show the mock being created in the constructor. Maybe I'm still not getting something, but at least it's working... for now. :-)

Proper way to test a Symfony2 Service with Doctrine

I'm struggling to find the correct way to unit test my symfony 2 services which use doctrine or other common services.
What i have done so far:
In my understanding the controller actions should:
be as short as possible
take the request
execute required methods from injected services
build a response out of this
is a service itself
To accomplish a lightweight action, i try to encapsule the logic into a separate service which gets injected into the controller.
This works nicely expect for testing everything.
Here my current code:
Controller
class SearchController
{
// search_helper, request and templating are controller-injected
protected $search_helper;
protected $request;
protected $templating;
// ...
public function searchAction()
{
$searchterm = strtolower($this->request->query->get('q'));
$result = $this->search_helper->findSamples($searchterm);
// Found a single result. Redirect to this page
if (is_string($result))
{
return new RedirectResponse($result, 301);
}
return new Response($this->templating->render('AlbiSampleBundle:Search:index.html.twig', array('results' => $result)));
}
}
SearchService
class SearchHelper
{
// doctrine, session and min_query_len are controller-injected
protected $doctrine;
protected $session;
protected $min_query_len;
// ...
public function findSamples($searchterm)
{
if (strlen($searchterm) < $this->min_query_len)
{
$msg = 'Your search must contain at least 3 characters!';
$this->session->getFlashBag()->add('error', $msg);
return false;
}
$em = $this->doctrine->getManager();
$results = $em->getRepository('AlbiSampleBundle:Sample')->findPossibleSamples($searchterm);
// Execute a more advanced search, if std. search don't delivers a result
// ...
return $results;
}
}
How can i test this code correctly?
The repository is tested with phpunit_db and a inmemory sqlite database ✓
The action can be tested through a simple functional test ✓
What's left is the logic in the search-service. e.g. the findSamples method
My first thought was to mock the dependencies (in fact that was one of the main aspects in separating the dependencies), but you not only have to mock the doctrine object, but also the entitymanager and the repository.
$em = $this->doctrine->getManager();
$results = $em->getRepository('AlbiSampleBundle:Sample')->findPossibleSamples($searchterm);
I think there must be a better solution. Not only would this mocking need many LOCs, it also doesn't feel right. The test would be unnecessarily coupled really tight to the SUT.
EDIT
Here is a sample test i came up with. Using mock objects.
The test won't work. I realized it would take much more mock-objects and i got the feeling this isn't the right way.
The test fails because SessionMock->getFlashbag doesn't return a flashbag with add method.
doctrine->getManager returns no EntityManager. The EntityManager has no getRepository method. And the repository is missing findPossibleSamples.
class SearchHelperTest extends \PHPUnit_Framework_TestCase
{
private $router;
private $session;
private $doctrine;
public function setUp()
{
parent::setUp();
// ...
}
public function testSearchReturnValue()
{
$search_service = $this->createSearchHelper();
$this->assertFalse($search_service->findSamples('s'));
}
protected function createSearchHelper()
{
return new SearchHelper($this->doctrine, $this->router, $this->session, 3);
}
protected function getDoctrineMock()
{
return $this->getMock('Doctrine\Bundle\DoctrineBundle\Registry', array('getManager'), array(), '', false);
}
protected function getSessionMock()
{
return $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array('getFlashBag'), array(), '', false);
}
protected function getRouterMock()
{
return $this->getMock('Symfony\Component\Routing\Router', array('generate'), array(), '', false);
}
}
Hope the community can help me, writing well tested code :)
cheers
For your specific example I would argue that the validation of the $searchterm doesn't really belong in your service - at the very least a service should never depend on the session. There are ways you could move the session out of the service and leave the validation in but personally I would use symfony validation for this i.e. have a SampleSearchType for the form that uses itself as the data class and hang the validation off that in validation.yml (or using annotations as appropriate).
Once that validation is taken out, what's left from your question is another findX() method to be added to the repository (there's no reason why repository methods can't call and build on each other) which you already know how to test.
Having said that, I still agree that with Symfony there is a general issue of how to test services in isolation from injected services. With respect to testing in isolation from the persistence layer I've avoiding trying to do this so far. My business layer services are so tightly coupled with the persistence layer that the cost of trying to test them independently is not worthwhile (what logic there is consists mainly of making related db updates or sending emails for which symfony provides it's own decoupling mechanism). I'm not sure if this is because I'm doing it wrong or because the apps I'm working on are light on business logic!
To isolate service tests from dependencies other than persistence I've tried:
Overriding service classes with mocked versions in the configuration. Issue - you don't want to do this for functional tests which means you have to have tests scripts which update the configuration and/or change the config to run individual tests. Advantage - you can run the same test as an isolated unit test and as an integration test by flipping the config
(Warning: nasty hack!) providing a setter method to replace an injected service with a mocked version from the test program.
(Not yet tried) Directly instantiate the service being tested, passing mock dependencies in on construction.
With respect to isolating from the persistence layer the only approach that makes sense to me is to abstract it out of the service to be tested into a wrapper service which contains no additional logic. The wrapper service could then be mocked using one of the above approaches (or hopefully a better solution that someone else is going to suggest?!)
EDIT: to address the issue of complexity of mocking dependencies - very occasionally this may be unavoidable but in general this is an indication that the design needs revisiting. This is one of the strengths of TDD - it strongly encourages simplified design and decoupling of components:
No service should need to be dependent upon the session object. This is not good practice and can always be avoided. Worst case the example method could return mixed values and if the result is not an array it's assumed to be an error message, although there are better alternatives.
Sometimes dependencies are unnecessary (code more naturally belongs elsewhere) or too general (I would question the necessity of injecting high level objects like doctrine or e.g. the container into anything other than test helpers).
If there is a complex dependency to mock (such as on multiple classes from the persistence layer) abstract it out into a wrapper which is far simpler to mock than the complex dependency.

PHPUnit extension Selenium 2 (webdriver) and multiple browsers

I wrote a test case with PHPUnit extension Selenium2TestCase. It does work nice, but I can't figure out how make automatically run this test on various browsers.
There is a method setBrowser() which it only works in setUp() method. I thought about something like this:
/**
* #dataProvider browsers
*/
public function loginTest($browser) {
$this->setBrowser($browser);
// tests...
}
But I does not work. Try runs default browser (Propably I have a small mess with Safari, last time uses firefox)
RuntimeException: Safari could not be found in the path!
Please add the directory containing ''Safari'' to your PATH environment
variable, or explicitly specify a path to Safari like this:
*safari /blah/blah/Safari
PS. SeleniumTestCase (not based on webdriver) provides a xml config where we can specify browsers. Selenium2TestCase does not support it.
Any suggestions are welcome. Thanks.
Marcin
something like this
class WebTestCase extends \application\components\test\ExWebTestCase
{
// default params
public $parameters = array(
'host' => 'localhost',
'port' => 4444,
'seleniumServerRequestsTimeout' => 30000,
'timeout' => 30000,
);
// list of browsers with per-browserconfig
public static $browsers = array(
array(
'browserName' => 'firefox',
),
array(
'browserName' => 'chrome',
),
array(
'browserName' => 'safari',
),
array(
'browserName' => 'internet explorer',
'host' => 'some IP of VirtualBox with IE'
)
);
}
The fix for this really depends on what your data file looks like. If you could post that we could help you more.
For the time being I'm assuming since phpunit by nature is a one-at-a-time unit testing framework that you're not attempting to run multiple browsers simultaneously, but just want to reserve the option to change browsers as you see fit.
You're right that you should be using setBrowser in setUp. When executed PHPUnit will always run setUp first, and tearDown() last. A good practice here is to make your own custom unit-test-case class where you can customize these methods.
class customUnitTest extends PHPUnit_Extensions_Selenium2TestCase {
public $browser = "firefox";
public function setUp() {
$this->setBrowser("*".$browser);
}
}
Now when you're writing a test extend your personal test class and set the browser accordingly
class newTest extends customUnitTest {
$this->browser = "safari";
public function testBlah {
blah blah...
}
}
setUp will be run on execution, and it will pull in the browser variable. By default you'll get firefox but if some tests are more appropriately tested on other browsers you have that option.
If you're looking to be able to change all browsers across all tests simultaneously you should look into assigning the browser value based on an environment variable.
class customUnitTest extends PHPUnit_Extensions_Selenium2TestCase {
try {
public $browser = getenv("SELENIUM_BROWSER");
} catch (Exception $e) {
public $browser = "firefox";
}
public function setUp() {
$this->setBrowser("*".$browser);
}
}
With this setup we can change the browser for every test that hasn't hard-coded the browser within itself by changing the environment variable SELENIUM_BROWSER. This way we can run the same code on different servers with different default browsers without having to re-write anything.
Note that multiple inheritence is not good practice. It can lead to brittle code and even security threats if you don't scope you methods/variables correctly. However in this case it's useful because we can define the PHPUnit framework methods as we please, and we get all the base selenium methods within our test. So to run a default selenium method we just write
$this->open("www.google.com");
This is a much different method than the general approach of assigning selenium to an object, as the test you write IS the selenium object, but it seems more php appropriate, especially for this use case.
To run in multiple browser check this link:
http://phpunit.de/manual/current/en/selenium.html
example 17.4(phpunit 3.7)
If ur running the testcase in localhost, use 'host ' =>'localhost'.
setBrowser() function is not need..
Use should user the $browsers property for defining multiple browsers, as defined above.

How to prevent Fixtures from being reloaded between tests with CakePHP and PHPUnit

How do you prevent a CakePHP 2.0 test case, which extends CakeTestCase (uses PHPUnit), from reloading fixtures between tests?
Background: There is a set of integration tests which we have written with CakePHP 2.0 using PHPUnit. We have extended the test case class from the standard CakeTestCase. For this set of tests, we have a bunch of fixtures setup to populate the data from the database. Naturally, these tests take a long time to run. Basically, all of the time is coming from Cake unloading and re-loading all of the fixtures between tests.
All of the tests act as READ only. We are just issuing find calls to the database and testing the logic among a set of class interactions based on those results. In fact, the tests can be boiled down to:
class ALongRunningTest extends CakeTestCase {
public $fixtures = array('app.class1', 'app.class2', ... 'app.class8');
/**
* #dataProvider provider
* #test
*/
public function checkCompositionLogic($val1, $val2, $val3) {
// internally calls class1 and class3
$data = $this->ModelX->generateComplexStructure($val1);
// internally calls other classes & models, which touch the
// other loaded fixtures
$results = $this->ModelY->checkAllWhichApply($val2, $data);
$this->assertEquals($val3, $results);
}
public function provider() {
return array(
array(stuff, stuff1, stuff2),
array(x_stuff, x_stuff1, x_stuff2),
array(y_stuff, y_stuff1, y_stuff2),
array(z_stuff, z_stuff1, z_stuff2),
array(a_stuff, a_stuff1, a_stuff2),
// More test cases
);
}
}
I've not been able to find anything on how to prevent this. I saw in the CakeTestCase class a public variable autoFixtures with a comment that says if you change it to false it won't load the fixtures. It makes a note stating that you have to load them manually. However, I see no documentation on how to actually load them manually.
Strictly speaking, CakePHP is correct in the way that it works. Tests shouldn't be dependent upon each other, so that database is reset between each test case. You could even argue that it should be reset between each test method, but the overhead would be even more noticeable.
However, since you're doing read only actions on the database, you could remove all the references to fixtures in your test cases and set up your database entries before you run the test suite (e.g. import it from an SQL file).
Or you could create a custom test suite that adds a whole load of data, e.g:
class AllTest extends CakeTestSuite {
public static function suite() {
self::loadDB();
$suite = new CakeTestSuite('All tests');
$suite->addTestDirectoryRecursive(TESTS . 'Case');
return $suite;
}
public static function loadDB() {
//Do some set up here using your models
}
}
The advantage of doing it that was is that if you ever had to introduce tests that do write to the database, you could run them under a separate test suite.

Resources