PHPUnit: How to force program exit on specific error - phpunit

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
}
}
}

Related

How to check for Exception property with PHPunit

I have the following exception:
<?php
namespace App\Exception;
class LimitReachedException extends \Exception
{
private ?\DateTime $resumeAt;
...getter/setter..
}
My PHPUnit check for this exception like this:
$this->expectException(LimitReachedException::class);
How can I check that a certain value is stored in the $resumeAt property as well?
Even though PHPUnit has a expectExceptionObject method that allows passing an exception instance, that is just a shortcut to expectExceptionMessage, expectException and expectExceptionCode.
One way to achieve your assertion as of now (current version of PHPUnit being 9.5.27) is to instead of using PHPUnit's methods of expecting that exception is to catch it yourself and then assert the different properties:
function testException () {
$expectedException = null;
try {
$foo->doSomething();
} catch (LimitReachedException $e) {
$expectedException = $e;
}
// Put your assertions outside of the `catch` block, otherwise
// your test won't fail when the exception isn't thrown
// (it will turn risky instead because there are no assertions)
$this->assertInstanceOf(LimitReachedException::class, $expectedException);
$this->assertSame('myExceptionProperty', $expectedException->getProperty());
}

phpunit custom teardown specific to my test

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();
}

symfony test database insert

I have a functional test that creates and persists some things in the database and I want to test that the correct number of items was inserted (there is a scenario where it currently inserts two instead of one).
In the controller everything seems to work and if I use the code below (in the controller) to debug it, I get the expected (wrong) value of "2":
$em = $this->getDoctrine()->getManager();
$fooRepo = $em->getRepository('CompanyProjectBundle:Foo');
$foos = $fooRepo->retrieveByBar(3);
echo count($foos); // Gives a result of 2
However, if I try something similar from within my Test class I get zero...
/**
* {#inheritDoc}
*/
protected function setUp()
{
static::$kernel = static::createKernel();
static::$kernel->boot();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager()
;
$this->em->getConnection()->beginTransaction();
}
/**
* {#inheritDoc}
*/
protected function tearDown()
{
parent::tearDown();
$this->em->getConnection()->rollback();
$this->em->close();
}
public function testFooForm()
{
// ... do some testing
$fooRepo = $this->em->getRepository('CompanyProjectBundle:Foo');
$foos = $fooRepo->retrieveByBar(3);
echo count($foos); // gives a result of ZERO
// ... more happens later
}
Is it getting a different entity manager or something like that? Should I be using some other method to get hold of the correct EM so I can then view the same data that the app is running from?
Everything's running inside a transaction (which is rolled back when the test client is destroyed), but that happens after the snippet shown above.
Ah... solved my own problem. I think I was getting the wrong EntityManager. I fixed it by getting the EntityManager via the client's container instead of the kernel's one:
public function testFooForm()
{
// ... do some testing
$clientEm = $client->getContainer()->get('doctrine.orm.entity_manager');
$fooRepo = $clientEm->getRepository('CompanyProjectBundle:Foo');
$foos = $fooRepo->retrieveByBar(3);
echo count($foos); // gives the correct result of 2
// ... more happens later
}

Dispatch not hooked to windows memory

Sometimes I get an error (exception):
java.lang.IllegalStateException: Dispatch not hooked to windows memory
What does it mean? How to prevent it?
This is a sample code that results in this error:
import com.jacob.activeX.*;
import com.jacob.com.*;
public class Hooked {
public static void main(String[] args) {
ActiveXComponent e = new ActiveXComponent("Excel.Application");
ActiveXComponent sh = new ActiveXComponent(
e.getProperty("ActiveSheet").toDispatch());
System.out.println(sh.getPropertyAsString("Name"));
}
}
That means that Dispatch = nothing using vba syntax, it's empty. The same dispatch that you receive with new Dispatch(). Unfortunately Jacob 1.17 does not provide any method to explicitly check whether the Dispatch is empty or not. So I see 3 possible solutions:
1) Use Variant.isNull() after receiving the Variant from COM call, before converting it to Dispatch. So it requires 1 additional line:
Variant vsh = e.getProperty("ActiveSheet");
if (vsh.isNull()) {
System.out.println("Null dispatch received.");
}
ActiveXComponent sh = new ActiveXComponent(vsh.toDispatch());
2) Catch IllegalStateException when using suspected Dispatch for the first time.
3) Write a custom isNull(Dispatch d) function
public static boolean isNull(Dispatch d)
{
try {
Dispatch.call(d, "");
}
catch (IllegalStateException ise) {
return true;
}
catch (ComFailException cfe) {
// that's ok, we didn't expect this call to succeed
}
return false;
}
In the specific example in the question the call was just a mistake, because Excel has no ActiveSheet if no workbook is open or created.

LINQ to SQL partial class OnValidate Changeaction.Delete

I have a LINQ partial class:
public partial class resp
{
public IEnumerable<RuleViolation> GetRuleViolations()
{
if (String.IsNullOrEmpty(respName))
yield return new RuleViolation("Responsibility name required", "respName");
yield break;
}
public bool IsValid
{
// Quick method for checking to see whether an object contains any RuleViolations
get { return (GetRuleViolations().Count() == 0); }
}
partial void OnValidate(ChangeAction action)
{
// Hook to LINQ to be notified before db is actually persisted .. and check to make sure resp is not used by respApprover or approvals
if (action == ChangeAction.Delete && ((respApprovers.Count() != 0 || approvals.Count() != 0)))
throw new ApplicationException("You cannot delete a responsibility that is in use");
if (!IsValid)
throw new ApplicationException("Rule violations prevent saving");
}
}
In my code I would like to be able to check whether a responsibility (dbo.resp) exists before it is deleted.
I am accomplishing this check with a hook to OnValidate as above ... if (action == ChangeAction.Delete) ... and returning an application exception if it fails.
Also, during normal validation, I would like to check to make sure rule violations aren't made (resp.respName in this case...)
So, normally I can just do a try { } catch { var errors = resps.GetRuleViolations() } and check whether I have validation errors. Of course, that doesn't know what action is being taken (Changeaction.Delete)
I guess my main question is, how can I have OnValidate return a reason rather than crash my whole app when the Changeaction is delete. Normally I would catch GetRuleViolations but if I put the 'if statement' in the GetRuleViolations ... it will always be true even during checks to see if the data was correct (in other words, respApprovers.Count() will matter even for checks on new insertions which I don't want to. I only want those count checks to be made on deletion)
Or possibly more simply stated: I want GetRuleViolations to tell me when someone attempts to delete resp that has respApprovers.Count() > 0 or approvals.Count() > 0 but only when the OnValidate Changeaction is delete.
Change GetRuleViolations to accept the ChangeAction parameter and add a parameterless version that calls it with ChangeAction.None (or Update or Insert, as appropriate). Then move your check in there as you want.
public IEnumerable<RuleViolation> GetRuleViolations( ChangeAction action )
{
if (action == ChangeAction.Delete) {
...
}
else {
...
}
}
public IEnumerable<RuleViolation> GetRuleViolations()
{
return GetRuleViolations( ChangeAction.None );
}

Resources