I have a new middleware that works as expected in the browser. However, when I try to trigger the middleware via a feature test, the handle() is never called.
I understand I can write a unit test for this middleware, and should. But should my actual feature test be moved to a browser test?
# Kernel.php
protected $middlewareGroups = [
'web' => [
MyMiddleware::class,
...
# MyMiddleware.php
public function handle($request, Closure $next)
{
dd('I can see this in the browser, but not in the Feature test. Doing some 302 magic here.');
# Feature Test
/**
* #test
* #return void
*/
public function my_new_test(): void
{
$this->get('/test')
->assertStatus(302)
->assertRedirect($vanityDomain->getFallbackRedirectUrl('/non-matching-path'));
}
it's working for me, not sure why yours is not working.
middleware
class MyMiddleware
{
public function handle($request, Closure $next)
{
if ('test' === $request->path()) {
return redirect('/test1');
}
return $next($request);
}
}
feature test
class ExampleTest extends TestCase
{
/**
* A basic test example.
*/
public function testBasicTest()
{
$this->get('/test')->assertStatus(302)->assertRedirect('test1');
}
}
Btw, unit and feature tests just a way of grouping your tests. Does not matter where you put, they all behave the same.
On laravel 8 I had to run php artisan optimize to get it working. Seems the route cache somehow needed to be cleared.
Related
If i define an #depends annotation like below, the test cannot be run if the createObjectBase Test has not run successfully before.
Sometimes i don't want to run the whole suite, but only the createObjectGeo Test.
How can i define that if i run createObjectGeo, codeception runs createObjectBase before it?
/**
*
*/
public function createObjectBase (AcceptanceTester $I) {
}
/**
* #depends createObjectBase
*/
public function createObjectGeo(AcceptanceTester $I) {
}
you should looking for the #before/#after annotations for this functionality
/**
*
*/
public function createObjectBase (AcceptanceTester $I) {
}
/**
* #before createObjectBase
*/
public function createObjectGeo(AcceptanceTester $I) {
}
please take a look at the documentation http://codeception.com/docs/07-AdvancedUsage#BeforeAfter-Annotations
The tests will be executed in the same order they will be written in the Cest file.
I am learning codeception and I wonder what is the difference between stubs and fixtures.
Both help me to load well-defined data and kepp tessts simple.
But when do I use
\Codeception\Util\Stub and when do I use
\Codeception\Util\Fixtures
So a stub is what Codeception uses to mock objects. In short, mocking is creating objects that simulate the behaviour of real objects.
Here is an example:
class UpdateBalance
{
public $balanceRepository;
public function __construct(BalanceRepositoryInterface $balanceRepository)
{
$this->balanceRepository = $balanceRepository;
}
public function subtract($amount, $id)
{
$updatedAmount = $this->balanceRepository->subtract($amount, $id);
if ($updatedAmount < 0) {
throw NegativeBalanceException($updatedAmount, $id);
}
return $updatedAmount;
}
}
class UpateBalanceTest extends PHPUnit_Framework_TestCase
{
public function testSubtractThrowsNegativeBalanceException()
{
$balanceRepository = Stub::make(
'BalanceRepositoryInterface'
array(
'subtract' => Stub::atLeastOnce(function() { return -100 })
)
);
$updateBalance = new UpdateBalance($balanceRepository);
$this->expectException(NegativeBalanceException::class);
$updateBalance->subtract(100, 1);
}
}
Note that we don't have a BalanceRepsository class. We have used Codeception stubs and pretended that the BalanceRepository class exists. By pretending it exists we can test the functionality of the UpdateBalance::subtract function by checking that the NegativeBalanceException is thrown.
Fixtures on the other hand would be for sharing test data throughout all your tests. If we use the UpdateBalance::subtract() example again, we could stress test the amount field ensuring it throws the correct exception depending on the amount being passed through:
// In some bootstrap or setup function
Fixtures::add('zero-amount', 0);
Fixtures::add('negative-amount', -1);
Fixtures::add('string-negative-amount', '-1');
class UpdateBalance
{
// ...
public function subtract($amount, $id)
{
if ($amount < 0) {
throw new
}
// ...
}
}
class UpateBalanceTest extends PHPUnit_Framework_TestCase
{
// ...
public function testSubtractThrowsCantSubtractNegativeAmountException()
{
$balanceRepository = Stub::make(
'BalanceRepositoryInterface'
);
$updateBalance = new UpdateBalance($balanceRepository);
$this->expectException(CantSubtractNegativeAmountException::class);
$updateBalance->subtract(Fixture::get('negative-amount'), 1);
}
}
Now we can use our pre-defined fixtures throughout all our tests. I would like to point out that using fixtures in the above example would probably be overkill, but for more complex test data like checking hexadecimal values are valid then it would be a lot more useful.
I am trying to find a way to run cleanup (DB) before running on each test. How could I do if I am using behat with mink? My current FeatureContext.php looks like this:
class FeatureContext extends MinkContext
{
/**
* Initializes context.
* Every scenario gets its own context object.
*
* #param array $parameters context parameters (set them up through behat.yml)
*/
public function __construct(array $parameters)
{
// Initialize your context here
}
}
Use the hooks in your context, read docs for Behat 3 or Behat 2. Example from Behat 3:
// features/bootstrap/FeatureContext.php
use Behat\Behat\Context\Context;
use Behat\Testwork\Hook\Scope\BeforeSuiteScope;
use Behat\Behat\Hook\Scope\AfterScenarioScope;
class FeatureContext implements Context
{
/**
* #BeforeSuite
*/
public static function prepare(BeforeSuiteScope $scope)
{
// prepare system for test suite
// before it runs
}
/**
* #AfterScenario #database
*/
public function cleanDB(AfterScenarioScope $scope)
{
// clean database after scenarios,
// tagged with #database
}
}
I searched, but couldnt find something.
So, I have route rules:
...
'/reg' => '/user/user/registration',
...
in
Yii::app()->request
I couldn find any route information.
So, how can I get in module init function and having only url, route lile
/reg -> user/user/registration
UPD
The route is only available from the running controller. By the time when a module is initialized the controller is not yet available, thus you can't find out the route there. (You can follow CWebApplication::processRequest to see what happens when a request is resolved up to the point where the controller is run.)
It depends on what you try to achieve, but you could override WebModule::beforeControllerAction to do something before the module controller is run.
Today (next day after my question), I could solve this.
I will try to explain:
As Michael wrote, we cant know in module in which controller we are.
But I net get just reversed route, so, its quite esay.
Yii::app()->getUrlManager()->parseUrl('/reg');
This will return my reversed route
user/user/registration
parseUrl
Solution for Yii 1.1.15 workes for me.
class HttpRequest extends CHttpRequest {
protected $_requestUri;
protected $_pathInfo;
public function setUri($uri){
$this->_requestUri = $uri;
}
public function setPathInfo($route){
$this->_pathInfo = $route;
}
public function getPathInfo(){
/* copy from parent */
}
public function getRequestUri(){
/* copy from parent */
}
}
The usage:
$uri_path = 'my/project-alias/wall';
/** #var HttpRequest $request */
$request = clone Yii::app()->getRequest();
$request->setUri($uri_path);
$request->setPathInfo(null);
$route = Yii::app()->getUrlManager()->parseUrl($request);
//$route equals 'project/profile/wall' etc here (like in route rules);
I'm using a slightly different sub-class of CHttpRequest:
class CustomHttpRequest extends \CHttpRequest
{
/**
* #var string
*/
var $pathInfo;
/**
* #var string
*/
private $method;
public function __construct($pathInfo, $method)
{
$this->pathInfo = $pathInfo;
$this->method = $method;
}
public function getPathInfo()
{
return $this->pathInfo; // Return our path info rather than the default
}
public function getRequestType()
{
return $this->method;
}
}
Then to call it (to create a controller, which is what I want):
$request = new CustomHttpRequest($uri, $method); // e.g. 'my/project-alias/wall' and 'GET'
$route = \Yii::app()->getUrlManager()->parseUrl($request);
list($jcontroller, $actionName) = \Yii::app()->createController($route);
Currently, I have a PHPUnit test case that extends PHPUnit_Extensions_SeleniumTestCase. Each function that starts requires a $this->setBrowserUrl() and defaults to starting a new Firefox browser window with each function call.
I want to have a test case that launches the browser for specific functions, but not launch the browser for other functions, as to save the resources and time it takes in opening and closing the browser. Is it possible for me to have such a file?
You best option is probably to create two separate test suites, one that uses uses Selenium commands and the other that does not use any Selenium functionality..
class BrowserTests extends PHPUnit_Extensions_SeleniumTestCase
{
protected function setUp()
{
$this->setBrowser('*firefox /usr/lib/firefox/firefox-bin');
...
}
public function testOne()
{
...
}
...
}
class NonBrowsterTests extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
...
}
public function testOne
{
...
}
...
}
Figured out a custom solution using PHPUnit annotations (and wrote a blog post about it!)
http://blog.behance.net/dev/custom-phpunit-annotations
EDIT: Adding some code here, as to make my answer more complete :)
In short, use custom annotations. In your setUp(), parse the doc block to grab annotations, and tag tests with different qualities. This would allow you to tag certain tests to run with a browser, and certain tests to run without.
protected function setUp() {
$class = get_class( $this );
$method = $this->getName();
$reflection = new ReflectionMethod( $class, $method );
$doc_block = $reflection->getDocComment();
// Use regex to parse the doc_block for a specific annotation
$browser = self::parseDocBlock( $doc_block, '#browser' );
if ( !self::isBrowser( $browser )
return false;
// Start Selenium with the specified browser
} // setup
private static function parseDocBlock( $doc_block, $tag ) {
$matches = array();
if ( empty( $doc_block ) )
return $matches;
$regex = "/{$tag} (.*)(\\r\\n|\\r|\\n)/U";
preg_match_all( $regex, $doc_block, $matches );
if ( empty( $matches[1] ) )
return array();
// Removed extra index
$matches = $matches[1];
// Trim the results, array item by array item
foreach ( $matches as $ix => $match )
$matches[ $ix ] = trim( $match );
return $matches;
} // parseDocBlock