Convert from SimpleTest to PHPUnit $this->UnitTestCase('message .....'); - php

Which action/method is in PHPUnit equal to in simpleTest:
$this->UnitTestCase('message .....')
Edit: Sorry for my mistake
I think what I asking about not exist in simple test directly its just our extended class.
But this method display message in begining of test- how its done with PHPUnit?
Thanks

I'm not a SimpleTest expert, but as far as I can tell that's the constructor for the UnitTestCase class. The equivalent in PHPUnit is PHPUnit_Framework_TestCase; you create your own tests by subclassing that and defining test methods. See the PHPUnit docs on writing tests for a quick howto and more info, but briefly, this is a complete PHPUnit test:
class MyTest extends PHPUnit_Framework_TestCase {
public function testSomething {
$this->assertTrue(MyClass::getSomethingTrue());
}
}
Update: to answer the revised question, the primary way to display messages in PHPUnit is on assertion failure. Every assert* function comes with an optional $message argument at the end, which you can use to display a custom message when that assertion fails.
If you want to always display a message, without having to fail an assertion, then you might try a straightforward print statement. It'll be interspersed with the test output, so this may not be the best (or nicest-looking) way of accomplishing what you want, but it'll certainly dump text to a console, which is what you seem to be asking.
If you're looking for some advanced debugging during unit-testing, you may also want to consider a logging framework of some kind (or even just a custom function that opens a file, prints to it, and closes the file again). That way, you preserve the integrity of the test output, but still get extra custom messages wherever you want them during your tests.

Related

How to test multiple PHP TypeErrors in one test?

I have following method:
MyClass {
public function __construct(array $data, ?SomeObject $object): void
}
I have following test:
public function testWrongDataTypeThrowsErrorException(): void
{
$this->expectException(TypeError::class);
//works as expected
new MyClass(null, new SomeObject);
//this is never called
new MyClass('string', new SomeObject);
}
And what happens is it only runs 1 test/assertion with the null parameter. The second time when I try to create MyClass with string, it never gets there. Why is that?
The same logic works if the code throws regular Exception.
I can write 2 separate methods instead, but thats very inconvenient.
Or is it stupid to test for this? My idea is that I want to test that the method accept array and only array. So if somebody would remove it from the method the test would fail.
Place the expectExeception... or exepectError... only directly before the action you want to assert it for.
If you need multiple, write multiple tests methods - for each exception and error individually.
The suggestion above is how it works with Phpunit out of the box and normally keeps your tests clean.
There is however also the option to just make use of PHP here and handle it like the following:
try {
new MyClass(null, new SomeObject);
$this->fail('an expected exception was not thrown');
} catch (TypeError $e) {
$this->addToAssertionCount(1);
}
Then the test-method is not interrupted and you can continue (you have to remove the earlier TypeError expectation as Phpunit will not detect it any longer).
If you feel this is more convenient than separated tests, go for it.
You can even add a helper function and pass a closure in so you can wrap it. Normally it's better to stick with clean tests IMHO, if you really must test for that.
From what you write in your question, I'd say the problem is solved in PHP since the array type-hint back in PHP 5.??? and I would not need to test for that (or you would also need to write tests for your tests, because also the tests can be changed).
If someone changes the method signature, they should have a valid reason for that. If they make a mistake, they perhaps didn't cover their refactoring with tests so the problem lies not in the code of the unit and is not to test for with unit tests.
Keep in mind, that a test should only have one reason to fail. So it's good to aim for that when writing a (unit) test.
Is it OK to have multiple asserts in a single unit test?

PHPUnit gives error: Target [Illuminate\Contracts\View\Factory] is not instantiable

I created a simple test for my new Laravel 7 application. But when I run php artisan test I get the following error.
Target [Illuminate\Contracts\View\Factory] is not instantiable.
The error doesn't appear when I go to the page in the browser.
$controller = new HomeController();
$request = Request::create('/', 'GET');
$response = $controller->home($request);
$this->assertEquals(200, $response->getStatusCode());
Although "Just write feature tests" may seem like a cop-out ("They're not unit tests!"), it is sound advice if you do not want to get bogged down by framework-specific knowledge.
You see, this is one of those problems that come from using facades, globals, or static methods. All sorts of things happen outside of your code (and thus your test code) in order for things to work.
The problem
To understand what is going on, you first need to know how Laravel utilizes Containers and Factories in order to glue things together.
Next, what happens is:
Your code (in HomeController::home() calls view() somewhere.
view() calls app() to get the factory that creates Views1
app() calls Container::make
Container::make calls Container::resolve1
Container::resolve decides the Factory needs to be built and calls Container::build to do so
Finally Container::build (using PHP's ReflectionClass figures out that \Illuminate\Contracts\View\Factory can not be Instantiated (as it is an interface) and triggers the error you see.
Or, if you're more of a visual thinker:
The reason that the error is triggered is that the framework expects the container to be configured so that a concrete class is known for abstracts (such as interfaces).
The solution
So now we know what is going on, and we want to create a unit-test, what can we do?
One solution might seem to not use view. Just inject the View class yourself! But if you try to do this, you'll quickly find yourself going down a path that will lead to basically recreating loads of framework code in userland. So not such a good idea.
A better solution would be to mock view() (Now it is really a unit!). But that will still require recreating framework code, only, within the test code. Still not that good.[3]
The easiest thing is to simply configure the Container and tell it which class to use. At this point, you could even mock the View class!
Now, purists might complain that this is not "unit" enough, as your tests will still be calling "real" code outside of the code-under-test, but I disagree...
You are using a framework, so use the framework! If your code uses glue provided by the framework, it makes sense for the test to mirror this behavior. As long as you don't call non-glue code, you'll be fine![4]
So, finally, to give you an idea of how this can be done, an example!
The example
Lets say you have a controller that looks a bit like this:
namespace App\Http\Controllers;
class HomeController extends \Illuminate\Routing\Controller
{
public function home()
{
/* ... */
return view('my.view');
}
}
Then your test[5] might look thus:
namespace Tests\Unit\app\Http\Controllers;
use App\Http\Controllers\HomeController;
use Illuminate\Contracts\View\Factory;
class HomeControllerTest extends \PHPUnit\Framework\TestCase
{
public function testHome()
{
/*/ Arange /*/
$mockFactory = $this->createMock(Factory::class);
app()->instance(Factory::class, $mockFactory);
/*/ Assert /*/
$mockFactory->expects(self::once())
->method('make')
->with('my.view')
;
/*/ Act /*/
(new HomeController())->home();
}
}
A more complex example would be to also create a mock View and have that be returned by the mock factory, but I'll leave that as an exercise to the reader.
Footnotes
app() is asked for the interface Illuminate\Contracts\View\Factory, it is not passed a concrete class name
The reason Container::make does nothing other than call another function is that the method name make is defined by PSR-11 and the Laravel container is PSR compliant.
Also, the Feature test logic provided by Laravel already does all of this for you...
Just don't forget to annotate the test with #uses for the glue that is needed, to avoid warnings when PHPUnit is set to strict mode regarding "risky" tests.
Using a variation of the "Arrange, Act, Assert" pattern
This is not how you test endpoints in Laravel. You should let Laravel instantiate the application as it is already setup in the project, the examples you can see here.
What you already wrote can be rewritten to something like this.
$response = $this->call('GET', route('home')); // insert the correct route
$response->assertOk(); // should be 200
For the test to work, you should extend the TestCase.php, that is located in your test folder.
If you're finding this in The Future and you see #Pothcera's wall of text, here's what you need to know:
The ONLY reason he's doing any of that and the ONLY reason you're seeing this in the first place in a Unit test is because he and you haven't changed from PHPUnit\Framework\TestCase to Tests\TestCase in the test file. This exception doesn't exist when you extend the test case that includes app().
My advice would be to simply extend the correct base test case and move on with your life.

Is it possible to use PHPUnit assertions without extending PHPUnit_Framework_TestCase?

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.

How to skip/mark incomplete entire test suite in PHPUnit?

Description
I have a TestSuite which I need to mark as skipped (the entire test suite - not the specific test cases within the suite).
class AllTests
{
public static function suite()
{
// this does not work same as within TestCase:
// throw new \PHPUnit_Framework_SkippedTestError("Out of order");
$Suite = new \PHPUnit_Framework_TestSuite(__NAMESPACE__);
$Suite->addTestSuite(translators\AllTests::cls());
$Suite->addTestSuite(TlScopeTest::cls());
$Suite->addTestSuite(TlNsTest::cls());
$Suite->addTestSuite(TlElementTest::cls());
$Suite->addTestSuite(TlItemTest::cls());
$Suite->addTestSuite(LangCodeTest::cls());
$Suite->addTestSuite(TlElemClassTagTest::cls());
return $Suite;
}
}
As you can see throwing the PHPUnit_Framework_SkippedTestError exception does not work. It is not caught by the PHPUnit, and is breaks the execution as any uncaught exception (which is understandable, as the suite() method is invoked while building tests hierarchy, before actually running the tests).
I've seen an exception class named PHPUnit_Framework_SkippedTestSuiteError, but have no clue how to take advantage of it. Any ideas?
Motivation
I have a TestSuite, which aggregates many test cases as well as other test suites. Almost every test in this fails, becouse of a change which I made in the core of my code.
The problem is that this package is not crutial, and is scheduled to be fixed later. Until then I have to run tests for every other package, but when I do the PHPUnit output becomes flooded with the errors coming from the package in question. This forces me to check every time if any of the failures is coming from any other package.
This, as you might suspect, is very susceptible to human error, i.e. I could miss a failure, which actually is important.
I could run only the test suite on which I am currently working, but I lose control of whether or not my changes in one package causes a failure in other package.
I do not want to comment out that test suite, because I'm afraid that I (or someone who will take over the code after me) could forget about it entirely.
Ok, so I'll put it together:
The AllTests class has to be refactored to extend PHPUnit_Framework_TestSuite.
This makes the class a fully valuable TestSuite and allows to implement a setUp method on the suite level.
The setUp method is called by the test runner (not by the builder), so it is safe to throw a SkippedTestError exception.
The corresponding method to do just that within a test suite is called markTestSuiteSkipped (notice the Suite in the method name).
The entire class would look like this:
class AllTests extends \PHPUnit_Framework_TestSuite
{
protected function setUp()
{
$this->markTestSuiteSkipped("zzz");
}
public static function suite()
{
$Suite = new self(__NAMESPACE__);
$Suite->addTestSuite(translators\AllTests::cls());
$Suite->addTestSuite(TlScopeTest::cls());
$Suite->addTestSuite(TlNsTest::cls());
$Suite->addTestSuite(TlElementTest::cls());
$Suite->addTestSuite(TlItemTest::cls());
$Suite->addTestSuite(LangCodeTest::cls());
$Suite->addTestSuite(TlElemClassTagTest::cls());
return $Suite;
}
}
The output is a pretty block of S letters, which definetly indicate, that we skipped a lot of tests. This cannot escape our attention and yet allows our tests to pass.
You could mark test as skipped.

Is there a way to turn off 'Stop On Failure/Error' for a specific test in PHPUnit?

I am developing an API documentation system, and want to dynamically check that each command has documentation attached.
The easiest way to do this is dynamically loop through each command and check for existing documentation to match it.
My code looks like this:
public function testMissingDocs()
{
foreach ($aCommands as $sKey => $aOptions)
{
$this->assertNotNull($oDocs->get($sKey));
}
}
The problem with this is the StopOnFailure/Error feature of PHPUnit which stops the test after the first assertion fails. I understand the reasons for this functionality and I want to keep it on for the majority of my test cases, but for dynamic assertions/tests it makes things a bit hard.
Is there a way to disable it on a per-test basis so I can check each command in this test?
You can use a data provider to split the single test into as many tests as you have commands.
/**
* #dataProvider getDocsForAllCommands
*/
public function testEveryCommandHasDocs($sKey)
{
$this->assertNotNull($oDocs->get($sKey));
}
public function getKeysForAllCommands()
{
return array_keys($aCommands);
}
If the documentation for a particular class or method is missing, that would represent a problem with that class, not with the method to retrieve the documentation.
Although it is probably easier to combine all of the documentation into a single test, that does not follow unit testing best practices (and hence is why the PHPUnit framework is working against you rather than for you).
I would suggest one of two approaches to rectify the issue:
Refactor your tests so that each class and/or method has its own documentation check (there are a few ways you can run multiple tests in one execution).
Use a tool such as PHP_CodeSniffer instead, as lack of documentation could be considered a convention – rather than a functional – defect.

Categories