phpunit custom teardown specific to my test - phpunit

I have some setup specific to my test in a class. Since it is specific to my test, I have added to it to the top of my test function. The cleanup is added to the end of the test function. The problem when the test fails and the cleanup does not gets executed. Is there a PHPUnit way to specify a custom teardown specific to my test function. I looked at the PHPUnit manual which specifies teardownAfterClass and tearDown, but both does not solve my problem. The function teardownAfterClass will run only once at the end of the class. The function teardown runs after each test, but I do not want to do any cleanup if my specific test function was not executed.
What is the PHPUnit way of creating custom teardown function for my test?
Here is the code which I use to make sure that the cleanup specific to the test always happen, but it is ugly as it needs to put the actual test in a separate function and needs try/catch block. Is there is a PHPUnit specific way to handle it? Something like dataProvider specific to function will be great, which always gets executed after the test irrespective of failure or success.
class testClass {
public function test1() {
self::setupSpecificToTest1();
try {
// actual test without cleanup
$this->_test1();
} catch (Exception $e) {
self::cleanupSpecificToTest1();
throw $e;
}
self::cleanupSpecificToTest1();
}
public function test2() {
// some code which does not need any setup or cleanup
}
private function _test1() {
// some test code
}
}

Implement intelligent tearDown (it's closer to PHPUnit's approach to running tests)
You can check for the specific test name inside tearDown method to alternate its behaviour accordingly. Getting test name can be done via $this->getName() in the test class.
Try something like:
...
public function test1() { ... }
public function test2() { ... }
public function tearDown()
{
if ($this->getName() === 'test1')
{
// Do clean up specific to test1
}
parent::tearDown();
}

I have tried this, it worked for me after my test.
public function tearDown()
{
$this->webDriver->close();
}

Related

MSTest - Integration tests - use value of another test

I know that unit tests should run isolated and should never depend on other unit tests.
However, I also write some integration tests with MSTest and sometimes they produce a result that I would like to reuse in another test.
For example:
Creating a user
Searching this user from the database
Deleting the user
Each of those points would be an integration test for me, so I would like to write methods that look like this:
User _myNewUser;
[TestMethod]
public void CreateAUserTest()
{
//User gets created here somehow....
_myNewUser = successfullyCreatedUser;
}
And this test should run after the preceeding test:
User _myNewUser;
[TestMethod]
public void SearchingUserTest()
{
var user = searchUser(_newUser.GetName());
//Assert that user is not null
}
You can see that I use the value of the first test in the second test.
With a playlist I could make sure that both tests run in the correct order.
However, in VS 2022 each test gets executed in isolation, so what I am trying to do does not work.
_newUser is always null if I run the second test, even if the first test was a success.
Is my idea bad in general?
If not: How can I use the produced data of a test in another test?
I usually extract the contents of a test like that into a separate staging function that does not contain the [TestMethod] attribute, so that I can reuse it to stage other tests.
private void Stage_CreateAUser()
{
//do work from CreateAUserTest()
}
[TestMethod]
public void CreateAUserTest()
{
Stage_CreateAUser();
}
private void Stage_SearchingUser()
{
//do work from CreateAUserTest()
}
[TestMethod]
public void SearchingUserTest()
{
Stage_CreateAUser();
Stage_SearchingUser();
}
Etc...

Silverstripe 3.7 Unit Tests - tearDownOnce() and setUpOnce() do not trigger

Silverstripe 3.7.6, PHP 7.3, PHPUnit 4.8
We have a bunch of existing unit tests that use SapphireTest->setUpOnce() and SapphireTest->tearDownOnce(). The latter is used to destroy test databases once complete. Which is how we found that these methods were not firing.
Simple test that simulates
<?php
class MyTest extends SapphireTest {
protected $usesDatabase = true;
public function setUp()
{
echo "setup \n";
parent::setUp();
}
public function tearDown()
{
echo "teardown \n";
parent::tearDown();
}
public function setUpOnce()
{
echo "setup once \n";
parent::setUpOnce();
}
public function tearDownOnce()
{
echo "teardown once \n";
parent::tearDownOnce();
}
function testSomething()
{
// My test
}
}
Output when test is run:
PHPUnit 4.8.36 by Sebastian Bergmann and contributors.
.setup teardown
Time: 6.38 seconds, Memory: 80.50MB
OK (1 test, 0 assertions)
Is there something that needs to be done in the test so these functions are fired at the start and end of the whole test class per docblock?
UPDATE:
See convo below. It was suggested that I use PHPUnit's setUpBeforeClass and tearDownAfterClass methods instead. These fired as expected. However they are static methods so I had no access to $this to use supporting SapphireTest or custom methods in the class. So I had to use these PHPUnit methods to instantiate an instance of the test class they sit in, then call my existing setUpOnce and tearDownOnce methods. I used late static binding's static() keyword here as I might move these calls into a parent class for my tests all to use. A bit hacky, but it seems to work.
Assuming my test code above, below is the addition to make:
public static function setUpBeforeClass() {
$object = new static();
$object->setUpOnce();
}
public static function tearDownAfterClass() {
$object = new static();
$object->tearDownOnce();
}

What should be really done in tearDown() in PhpUnit

Just assigning null to the obj is what done in tearDown().What all the entries created in db by other function while testing that will be deleted and for that we should write in tearDown() this was I thought.
protected function setUp()
{
$this->XSCCategoryModelObj = new XSCCategoryModel();
}
protected function tearDown()
{
$this->XSCCategoryModelObj =null;
}
I believe that this section from documentation answers on your question. Also, this part with explanation can help:
The setUp() and tearDown() template methods are run once for each test method (and on fresh instances) of the test case class.
In addition, the setUpBeforeClass() and tearDownAfterClass() template methods are called before the first test of the test case class is run and after the last test of the test case class is run, respectively.

PHPUnit: How to force program exit on specific error

How can I force PHPUnit to stop running completely and exit when a specific condition (an error of my own choosing) is met? Effectively, what I need is something like the below, except that in reality PHPUnit traps the exit() and continues running instead of exiting.
// PHPUnit does not alter existing but empty env vars, so test for it.
if (strlen(getenv('APP_HOME')) < 1) {
$this->fail('APP_HOME set but empty.');
exit(1); // <-- Does not work.
}
Note: I want to continue running normally for other errors and failures, hence setting stopOnError="true" or stopOnFailure="true" in my XML file is not what I need.
I think you can achieve this by doing a few overrides and adding some custom behaviour to a base test case class.
EDIT:
As found by the OP after running the below code, calling exit(1); rather than $result->stop() will cause correct termination of the test at that point.
Try the following:
class MyBaseTestCase extends \PHPUnit_Framework_TestCase
{
// Test this flag at every test run, and stop if this has been set true.
protected $stopFlag = false;
// Override parent to gain access to the $result so we can call stop()
public function run(\PHPUnit_Framework_TestResult $result = null)
{
$result = parent::run($result);
if ($this->stopFlag === true)
{
//$result->stop(); // Stop the test for this special case
exit(1); // UPDATED: This works to terminate the process at this point
}
return $result; // return as normal
}
}
Then in a test case class:
class MyTestCase extends MyBaseTestCase
{
public function testThisStopsPhpunit()
{
if (strlen(getenv('APP_HOME')) < 1) {
$this->fail('APP_HOME set but empty.');
$this->stopFlag = true; // Stop further processing if this occurs
}
}
}

Using isEnabled() for a test control

I am using PageObject model in developing test scripts. I am trying to put into test code control of a current situation. In my case it is an element enabled or not. The source code below.
#FindBy(linkText="Overview")
private WebElement overviewTab;
public boolean checkOverviewTabVisable() {
return overviewTab.isEnabled();
}
if(customerSitePage.checkOverviewTabVisable()) {
customerSitePage.getOverviewTab();
} else {
sitesTabsList.getBuyerSite();
customerSitePage.getOverviewTab();
}
Script hangs in the line of checkOverviewTabVisable() method call and no any error. Something weird in isEnabled() behavior. Has anyone used this method for test control?

Resources