How to sort the tested functions programmatically in PHPUnit? - php

I'm using PHPUnit to test a class that has many functions.
The PHPUnit framework runs the test functions from the top to the bottom.
The question is: How can I run the test functions in a specified order without reorder then in the source code.
To clarify the issue, imagine we have 5 test functions;
testFunc1
testFunc2
testFunc3
testFunc4
testFunc5
The framework will run testFunc1 then testFunc2 until it reaches testFunc5.
However, I want to run testFunc3 then testFunc1 then testFunc5 then testFunc2 then testFunc4 without reordering them in the source file.

PHPUnit will execute the tests in the exact order they are written in your *_TestCase class.
Every one of those tests should be able to run in isolation and not depend on some other test beeing executed before it.
If you have issues testing against a Database I'd suggest using somethig like this:
class MyTest extends PHPUnit_Framework_TestCase {
public function setUp() {
// REPLACE INTO testDb (ID, NAME, VALUE) VALUES (1001000, 'testing', 'value')
$this->db = $db_connection;
}
public function tearDown() {
// DELETE FROM testDb WHERE ID > 10010000 // or something like this
}
public function testSelect() {
$this->assertSame("value", $this->db->getId(100100));
}
/**
* #depends testSelect
*/
public function testInsert() {
$this->db->insertById(1001111, "mytest", "myvalue");
$this->db->getId(1001111);
}
/**
* #depends testSelect
*/
public function testDelete() {
$this->db->deleteById(1001000);
$this->assertNull($this->db->getId(10010000);
}
// and so on
}
The setUp() method will be run before every testcase and make sure all the values most testcases need are there, the tearDown() will clean up after the testsuite.
The #depends annotation will make sure that the insert test isn't run when the select test fails. (If you can't load values then inserting new ones and getting those can't work ether, no need to try it).
For that also check the manual on test dependencies

The whole point of unit tests are actually in the name itself, Unit Testing. They function on their own and have no dependencies whatsoever on each other. If you code your tests right, order of execution should not matter.
If it is a matter of a database issue, make sure you have a clean database before every test.

Right now there's no way to do it short of reordering the functions in the file. There's a feature request to use the #depends annotations to reorder the tests, and the PHPUnit author has expressed a desire to do it. You can comment on the request at PHPUnit's github tracker.

Related

How I can have on each phpunit test specific tearDown logic?

On my phpunit tests I need to run a cleanup functionality for each test seperately for example:
use PHPUnit\Framework\TestCase;
use MyApp\Database\DatabaseEntryDataGenerator;
use MyApp\Database\Record\User;
use MyApp\Database\Record\Emails;
class MyEmailTest extends TestCase
{
public function testEmailValid()
{
/**
* #var EmailEntries
*/
$emails=DatabaseEntryDataGenerator::table(EmailEntries::TABLE)->generate();
//Do test
$emails->delete();
}
public function testEmailValidOnUser()
{
/**
* #var User
*/
$user=DatabaseEntryDataGenerator::table(User::TABLE)->generateSingle(1);
/**
* #var EmailEntries
*/
$emails=DatabaseEntryDataGenerator::table(EmailEntries::TABLE)->generateMultiple();
//Do test
$emails->delete();
$user->delete();
}
}
Assume that fictional DatabaseEntryDataGenerator generates data on database and the fictional EmailEntries and User represent the record data. The method delete on EmailEntries and User.
But if during the development of the test something brak mucase the delete that is the cleanup code may be deleted. Also the generic tearDown is way to generid and does not allow to specify a specific method indicain a test the required tearDown logic. For example in my case the teardown logic on testEmailValid id Different on testEmailValidOnUser and if something breakd for example due to a typo, may end up not be able to run it aall hence not giving me the test isolation.
So how I can have per-test specific tear down logic that will be executed regardless the test is successfull or not?
The only way to do this is by using seperate classes. If you are in need of different teardown logic per test, you are either having architectural issues, are testing different functionality or simply don't have a good enough teardown strategy.
Often times, simple truncate logic will suffice during the teardown logic.
I would suggest using database transaction here. You really want to do the same thing in both tests: rollback any changes made to the database.
public function setUp(): void
{
MyDatabaseClass:startTransaction();
}
public function tearDown(): void
{
MyDatabaseClass:rollback();
}
This way you won't need to have special logic for each test, and if you are affecting more tables than expected, you will still be safe.

How to skip a Codeception cest test

I want to skip only one test in a codeception cest test.
Using Cept tests you can do $scenario->skip(); but does not work for Cest tests.
So I want to do something like this. Run the first test, but skip the second one.
Class MyTests{
public funtion test1(){
// My test steps
}
public function test2(){
$scenario->skip("Work in progress");
}
}
Thank you in advance.
the method you are looking for is called "incomplete".
$scenario->incomplete('your message, why skipping');
If you want to use Scenarios in Cest files, you can get it with the second parameter of your test method:
class yourCest
{
public function yourTest(WebGuy $I, $scenario)
{
$scenario->incomplete('your message');
}
}
Or you can use $scenario->skip('your message')
class yourCest
{
public function yourTest(WebGuy $I, $scenario)
{
$scenario->skip('your message');
}
}
Edit:
As already mentioned, the WebGuy is outdated and the annotations #skip or #incomplete are the way you should skip your tests in Cest files.
class yourCest
{
/**
* #skip Skip message
*/
public function yourTest(AcceptanceTester $I)
{
$I->shouldTestSomething();
}
}
I use the skip annotation for my unit tests.
/**
* #skip
*/
public function MyTest(UnitTester $I)
{
...
}
First of all, remember that which commands are available to you are going to depend on which modules and suites you have loaded. For instance, if you are doing integration tests with the default WordPress enabled YML:
$scenario->skip('your message');
won’t work in a Cest or Test out of the box, but will work in Acceptance.
In fact, generally this command will work with Cept tests [Cepts are usually Acceptance like tests, Cests and Tests are usually PHPUnit like OOP tests]. Also, you need to pass $scenario to your function. This isn’t clearly documented and I can’t get it to work in Cests. Don’t get me started on how bad a choice “$scenario” is as a keyword for a BDD framework! A “scenario” is a keyword in Gherkin referring to what is a “step object” in Codeception. In Codeception it seems to be used as a redundant form of “environment”, even though there are environments, suites, and groups already. Like most of this great framework, the docs and function names need to be redone by native English speakers, for the second time! [remember “web guy”? Damn sexists Europeans! Lol].
If you use the
/**
* #skip
*/
public function myTest(){
//this test is totally ignored
}
Annotation right above your function in a Cest or Test it will be skipped, and won’t even appear in the report. [REALLY skip it]. Use this if you want to compleately hide a test.
If you use the PHPUnit command directly:
public function myTest(){
throw new \PHPUnit_Framework_SkippedTestError('This test is skipped');
//this test will appear as a yellow “skipped” test in the report
}
This will generate a skipped test in the report, will turn yellow in the HTML report [--html]. Use this if you want to skip a test but notice in the report that it’s skipped.
Use PHPUnit_Framework_SkippedTestError. For example:
if (!extension_loaded('mongo')) {
throw new \PHPUnit_Framework_SkippedTestError(
'Warning: mongo extension is not loaded'
);
}
So to make your scenario skipped during test run :
You must have $scenario as second input param in your test
make call : $scenario->skip();
make call after skip : $I->comment("Reason why scenario skipped")

Laravel Schema::create ignoring table prefix when run from Unit Test

I have a unit test that is running a seeder I've constructed to create a new instance of one of my classes. When I run the seeder from Artisan, it runs without issue. However, when the unit test does it:
class MyClassTest extends TestCase {
public function setUp() {
// Setup application
parent::setUp();
$this->seed('MyClassSeeder');
}
... the following bit that gets run refuses to use the prefix!
class MyClass extends Base {
// Switch to the proper DB prefix
protected function prefix() {
DB::connection('main')->setTablePrefix($this->id . '_');
return $this;
}
// This is run by the seeder, everything up to here works fine...
public function setupDatabase() {
$this->prefix();
// This returns "1001_", which is correct
Log::info(Schema::connection('main')->getConnection()->getTablePrefix());
// Creates "myTable" instead of "1001_myTable"
if(!Schema::connection('main')->hasTable('myTable')) {
Schema::connection('main')->create('myTable', function($table) {
...
As it works fine when I run it manually, I'm not even sure where to start looking. It's even returning the proper prefix, just not using it. Has anyone else seen this? Is it reproducible? Is the testing environment (automatically used by unit tests) turning something off other than the filters, which I've already re-enabled?
Any help would be appreciated. Even a reproduction would at least tell me it's not our code. We've extended quite a bit, but there should be no major changes to the Laravel codebase on our install.
FURTHER TESTING
So I continued my testing, and found what appears to be a bug, though I'm going to look into it further, I'm posting it here in case it jogs anyone's memory for a similar issue:
I modified
Log::info(Schema::connection('main')->getConnection()->getTablePrefix());
to be
Log::info('Grammar Prefix: ' . Schema::connection('promotion')->getConnection()->getSchemaGrammar()->getTablePrefix());
Log::info('Connection Prefix: ' . Schema::connection('promotion')->getConnection()->getTablePrefix());
And found that while the Connection Prefix line works properly as it did before, the Grammar's Prefix is non-existent. The Grammar and Connection prefixes don't match. While I can work around this, I'd like to know why it's held consistent when I run it in any way other than through the unit testing class, but there's a mismatch here. Will be reporting as a bug soon if it appears that way to anyone else.
FINAL TESTING
From what I can tell, the code should get the initial settings from it's database config file, and then both the Blueprint and Schema should use the updated prefix when the line
DB::connection('promotion')->setTablePrefix($this->id . '_');
is run. I've worked around it by also running the line
DB::connection('promotion')->getSchemaGrammar()->setTablePrefix($this->id . '_');
Which seems to fix the issue. This shouldn't be necessary, and I see no reason the code I'm using should not have the desired effect. I'm considering this a bug, and will report it later today. I'll leave the question open for a bit in case I'm missing something, but I'll close it soon.
I've come to the conclusion this is a bug. There's no explanation why it would work in one environment but not the other unless Testing is doing something different than a default environment, and I can't find it.
PHPUnit doesn't change anything to how Laravel interacts with the database. Make sure that your configuration is not overridden when you run tests by any environment configuration
Also make sure you are setting and using the same connection . In the code you provided, you are setting up the prefix for connection promotion, yet you are using connection main
Here is an example that works, i tried to make it as similar as yours.
TestSeedTest.php
<?php
class TestSeedTest extends TestCase{
public function setUp() {
// Setup application
parent::setUp();
}
public function testSeeder(){
$this->seed('MyClassSeeder');
}
}
?>
MyClassSeeder.php
<?php
class MyClassSeeder extends Seeder {
private $id='123';
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$this->setupDatabase();
}
protected function prefix() {
DB::connection('promotion')->setTablePrefix($this->id . '_');
return $this;
}
// This is run by the seeder, everything up to here works fine...
public function setupDatabase() {
$this->prefix();
if(!Schema::connection('promotion')->hasTable('myTable')) {
Schema::connection('promotion')->create('myTable', function($table) {
$table->increments('id');
$table->string('test');
$table->timestamps();
});
}
}
}

Need advice on doing PHP unit test on complex methods within a class

I am a beginner of unit testing and having difficulty of testing an algorithm (which is executable by cron in actual implementation) that is within a PHP class with functions that don't have arguments as well as depending on other classes for data sources, e.g. this one:
class Mailing_System_Algo {
function __construct()
{
//Run the mailing system method
$this->execute_mailing_system();
}
function execute_mailing_system()
{
$Class_Data_Source = new Data_Source;
$groups = $Class_Data_Source->get_groups();
//Proceed only if groups are defined
if (!(empty($groups))) {
//rest of the algo codes here-very long and lots of loops and if statements
}
}
}
I would like treat the algo function like a blackbox so I won't be altering anything on their codes when I do the test. But how can I start testing them by feeding them inputs if the execute_mailing_system will run right away the moment the class is instantiated?
Supposing I would like to check if the algo will execute with or without groups, how can I provide an input in my unit test codes for $groups?
This is how my test case would look like:
class WP_Test_Mailing_System_Algo extends WP_UnitTestCase {
/**
* Run a simple test to ensure that the tests are running
*/
function test_tests() {
//no problem here
$this->assertTrue( true );
}
function test_if_algo_wont_run_if_no_groups_provided {
//Instantiate, but won't this algo run the construct function rightaway?
$Mailing_System_Algo = new Mailing_System_Algo;
//rest of the test codes here
//how can I access or do detailed testing of execute_mailing_system() function and test if it won't run if groups are null or empty.
//The function does not have any arguments
}
}
Of course there are lots of tests that I would be writing but I'm currently stuck on this one. This is the first test that I would need to execute. But I have a problem on how to get started doing this. I believe that once I get the technique right, the rest of the tests would be straightforward. I would appreciate any of your inputs and help..Thanks.
There is two flaws with the code that will hamper testing:
Constructor does Real Work
Hard-Coded Dependencies
You can improve this by changing the class to
class Mailing_System_Algo
{
public function __construct()
{
// constructors should not do work
}
public function execute_mailing_system(Data_Source $Class_Data_Source)
{
$groups = $Class_Data_Source->get_groups();
//Proceed only if groups are defined
if (!(empty($groups))) {
//rest of the algo codes here-very long and lots of loops and if statements
}
}
}
This is way, you can replace your Data_Source with a Mock or Stub, returning defined test values.
If this is not an option, have a look at the Test Helper extension:
https://github.com/sebastianbergmann/php-test-helpers¹
In particular, have a look at set_new_overload(), which can be used to register a callback that is automatically invoked when the new operator is executed.
¹ the Test-Helper extension is superseded by https://github.com/krakjoe/uopz

PHPUnit: How do I create a function to be called once for all the tests in a class?

I have a PHPUnit test case class (consisting of some test functions). I would like to write a oneTimeSetUp() function to be called once for all my tests in the class (unlike the standard setUp() function which is called once for each test in the class). In other words, I'm looking for a PHPUnit equivalent to the JUnit #BeforeClass annotation.
Same question with a oneTimeTearDown() function.
Is it possible to do so in PHPUnit?
Take a look at setUpBeforeClass() from section 6 of the PHPUnit documentation.
For the one time tearDown you should use tearDownAfterClass();.
Both this methods should be defined in your class as static methods.
setUpBeforeClass() is the way to do this if all of your tests are literally contained within a single class.
However, your question sort of implies that you may be using your test class as a base class for multiple test classes. In that case setUpBeforeClass will be run before each one. If you only want to run it once you could guard it with a static variable:
abstract class TestBase extends TestCase {
protected static $initialized = FALSE;
public function setUp() {
parent::setUp();
if (!self::$initialized) {
// Do something once here for _all_ test subclasses.
self::$initialized = TRUE;
}
}
}
A final option might be a test listener.
I came to this page with the same question, however the accepted answer is ran on all classes, and for me was not the correct answer.
If you are like me, your first "Integration test" is to clear out the DB, and run migrations. This gets yourself at a database baseline for all test. I am constantly changing migration files at this point, so setting up the baseline is truly part of all tests.
The migration takes a while, so I do not want it run on all tests.
Then I needed to build up the database testing each piece. I need to write an order test, but first I need to create some products and test that, then I need to test an import fuction.
So, what I did is SUPER easy, but not explained extremely well on the internet. I created a simple test to setup the database. Then in your phpspec.xml file add a testsuite....
<testsuite name="Products">
<file>tests/in/SystemSetupTest.php</file>
<file>tests/in/ProductTest.php</file>
<file>tests/in/ProductImportTest.php</file>
</testsuite>
And in the the SystemSetupTest.php ....
class SystemSetupTest extends ApiTester
{
/** #test */
function system_init()
{
fwrite(STDOUT, __METHOD__ . "\n");
self::createEM(); //this has all the code to init the system...
}
}
Then execute it like:
phpunit --testsuite Products
In the end, its a ton easier. It will allow me to build up my system correctly.
Additionally I am using laravel 5. When using setUpBeforeClass() I end up with bootstrap issues, which I am sure I can fix, but the method I use above works perfect.
The bootstrap option can be used on these cases.
You can call it from the command line
phpunit --bootstrap myBootstrap.php
Or put it in the XML file, like this:
<phpunit bootstrap="myBootstrap.php">
...
</phpunit>
Expanding on accepted answer:
None of the setUpBeforeClass(), tearDownAfterClass(), #beforeClass, #afterClass runs in the object context (static methods). You can work around that restriction by guarding any #before code with a static property instead, like so:
class MyTest extends PHPUnit\Framework\TestCase
{
private static $ready = false;
/**
* #before
*/
protected function firstSetUp()
{
if (static::$ready))
return;
/* your one time setUp here */
static::$ready = true;
}
}
It can't be used for #after, however, because there's no way of saying when the last test was called.

Categories