setUp/tearDown fixtures when unit testing in Laravel - laravel

I'm trying to write a unit test that checks attribute method that uses base_path() helper, however, I'm getting an exception: Call to undefined method Illuminate\Container\Container::basePath().
The full stacktrace is below:
\vendor\laravel\framework\src\Illuminate\Foundation\helpers.php:179
\app\Message.php:47
\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasAttributes.php:432
\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasAttributes.php:333
\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasAttributes.php:306
\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php:1279
\tests\Unit\MessageTest.php:59
I've tracked it down to using setUp and tearDown fixtures - even if I've got:
public function setUp()
{
//$_SERVER['DOCUMENT_ROOT'] = dirname(__FILE__) . "/../..";
}
public function tearDown()
{
//unset($_SERVER['DOCUMENT_ROOT']);
}
I'm getting the abovementioned error. If I remove the fixtures entirely, the error goes away.
After I've replaced the given methods with setUpBeforeClass and tearDownAfterClass the error goes away, but I'd like to know what is causing it.
As far as I'm aware of this is vanilla Laravel 5.4 installation (5.4.36 exactly), but it has additional libraries installed (and I'm not really able to say what libraries). I've not setup the phpunit.xml file, but to be fair I'd not know what to look for.
I've tested it with fresh installation of Laravel (same version) and it does happen straight out of the box (with untouched phpunit.xml file); on version 5.5 it doesn't.

Try calling the parent class setUp and tearDown methods inside your own.
Like so:
public function setUp()
{
parent::setUp();
//$_SERVER['DOCUMENT_ROOT'] = dirname(__FILE__) . "/../..";
}
public function tearDown()
{
parent::tearDown();
//unset($_SERVER['DOCUMENT_ROOT']);
}

Make sure that you are calling the parent setUp() and tearDown() first before you continue down. See code down here
public function setUp()
{
parent::setUp();
//$_SERVER['DOCUMENT_ROOT'] = dirname(__FILE__) . "/../..";
}
public function tearDown()
{
parent::tearDown();
//unset($_SERVER['DOCUMENT_ROOT']);
}

Related

Why I can't inject a class in a test case with dusk

I am testing my laravel project with dusk.
I am trying to inject a dependency in a test like that :
....
class PasswordLostTest extends DuskTestCase
{
private $passwordResetRepository;
public function __construct(PasswordResetRepository $passwordResetRepository)
{
$this->passwordResetRepository = $passwordResetRepository;
}
.....
When I run the test with php artisan dusk .\tests\Browser\PasswordLostTest.php, I have this error :
Fatal error: Uncaught ArgumentCountError: Too few arguments to function Tests\Browser\PasswordLostTest::__construct(), 0 passed in D:\Workspace\classified-ads-mpa\vendor\phpunit\phpunit\src\Framework\TestBuilder.php on line 138 and exactly 1 expected in D:\Workspace\classified-ads-mpa\tests\Browser\PasswordLostTest.php:20
What is the problem ? Is it because I am in a test script ?
TestCases are not instantiated by the container. Secondly your app is not bootstrapped before setup has been called, the constructor is called first. Making your example technically impossible. Instead use resolve() or app()->make(), in the setup method to do what you want.
Your app is bootstrapped to the parent test cases setup, so calling parent::setUp(); is essential.
public function setUp()
{
parent::setUp();
$this->passwordResetRepository = resolve(PasswordResetRepository::class);
}
Here is the solution :
public function setUp(): void
{
parent::setUp();
$this->passwordResetRepository = resolve(PasswordResetRepository::class);
}
WARNING : the void is mandatory. That's why it did not work !
Many thanks to mrhn who guided me for this problem !

Laravel 8 Testing - using RefreshDatabase but table not found

I'm trying to refactor a class and having problems testing it when I place certain code in the __constructor method and it throws an error that table not found within the test but works outside of tests.
I know this means that in the testing environment the table has yet to be created and although I'm using RefreshDatabase within the test it appears that at the point the class I'm testing initialises and attempts to access the database it's not ready. So I'm either doing something in the constructor I shouldn't or I'm missing something in my test structure.
Here's the basics of the class I'm tryting to test:
class PlayerRounds
{
use EclecticPresenter;
private RoundRepository $roundRepository;
private CourseRepository $courseRepository;
private $courseHoles;
public function __construct(RoundRepository $roundRepository, CourseRepository $courseRepository)
{
$this->roundRepository = $roundRepository;
$this->courseRepository = $courseRepository;
$this->init();
}
private function init()
{
$this->courseHoles = $this->courseRepository->all();
}
/**
* generates eclecic rounds for each league the player is in
* #param Player $player
*/
public function getAllEclecticRounds(Player $player)
{
$allEclecticRounds = collect();
$leagues = $player->league()->where('league_type', 'eclectic')->get();
$leagues->each(function ($league) use ($player, $allEclecticRounds) {
$newRound = $this->getPlayerEclecticRound($player, $league);
$allEclecticRounds->put($league->id, $newRound);
});
return $allEclecticRounds;
}
The test fails at the init() method. The fetch $this->courseHoles = $this->courseRepository->all(); the the test fails with a table not found error if it's within the constructor, It works if I place this piece of code within each method that needs it but means I call it often rather than once.
Here's my test:
class PlayerRoundsTest extends TestCase
{
use RefreshDatabase;
use EclecticTestHelper;
use WithFaker;
private $playerRounds;
protected function setUp(): void
{
parent::setUp();
$this->seed(CourseTableSeeder::class);
$this->playerRounds = app()->make(PlayerRounds::class);
}
/**
* #test
* #covers PlayerRounds::getAllEclecticRounds
* #description:
*/
public function it_returns_an_eclectic_round_for_all_leagues()
{
$player = Player::factory()->has(League::factory()->count(3))->create();
foreach ($player->league()->get() as $league) {
for ($x = 0; $x <= 3; $x++) {
$this->createScores($league, $player);
}
}
$result = $this->playerRounds->getAllEclecticRounds($player);
$this->assertCount(3, $result);
$result->each(function($collection) {
$this->assertCount(1, $collection);
});
Are there any ideas how I can initiate the class correctly and get the test set up correct and ensure the database is ready for the test. I assumed using RefreshDatabase was the correct approach and I had things in the correct order.
Thank you
**update
If I change the constructor to this:
public function __construct(RoundRepository $roundRepository, CourseRepository $courseRepository)
{
$this->roundRepository = $roundRepository;
$this->courseRepository = $courseRepository;
}
and then place the code that calls on the database to the method used in the test back to this:
public function getPlayerEclecticRound(Player $player, League $league, $maxDate = null)
{
// FIXME: Initiate at start in constructor but fails in tests
$this->courseHoles = $this->courseRepository->all();
//rest of code removed for brevity
}
This then passes the test.
This class needs the data in $this->courseHoles for a number of methods to work so I'm aiming to just call this once at initialization rather than every time I access the method as it is now but can' get it to work in a testing environment.
note I'm using a mysql database on the server but a sqllite memory database in testing
###update
Ok, after a bit of playing around the error is being caused by the loading of a custom artisan command I created. That command class has a dependancy of another class which in turn calls on the class I'm tresting.
I removed the command from Kernel.php as follows:
protected $commands = [
Inspire::class,
FixtureReminder::class,
SmsFixtureReminder::class,
UpdateMigrationTable::class,
// EclecticUpdate::class,
// MatchplayUpdate::class,
CleanTemporaryFiles::class,
AuthPermissionCommand::class
];
So - am I right in assuming for test purposes this is going to be impossible to isolate without changing this each time? This all relates to dependencies and the order in which CreateApplication works but I don't know enough to work around this.

folder structure for scraping in Laravel, using Goutte

I am a bit confused about my folder structure for the scraping code. Using console/commands, not the controller. So, in the handle function I am writing the whole scraping code. But should I suppose to do that? Or... what is the best approach for this?
UPDATED
If I understand correctly the answer below. It should look like this right now.
calling services
class siteControl extends Command
{
protected $signature = 'bot:scrape {website_id}';
protected $description = 'target a portal site and scrape';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$website_id = $this->argument("website_id");
if ($website_id == 1) {
$portal = "App\Services\Site1";
}
$crawler = new $portal;
$crawler->run();
}
}
in handle method
class Site1 extends Utility
{
public function __construct()
{
parent::__construct();
}
public function run()
{
echo "method runs";
}
}
abstract:
use Goutte\Client;
abstract class Utility implements SiteInterfaces
{
protected $client;
public function __construct()
{
$this->client = new Client();
}
}
interfaces:
namespace App\Services;
interface SiteInterfaces
{
public function run();
}
and finally, I should write the whole scraping code inside the run() method? Please correct me If wrong about this... I am searching the best solution.
A best practice would be to call a separate service from your command handle() method. That way you could reuse that same service in a controller for instance.
The technical version:
Your application is given a specific thing to do (a command if you will). This command comes from outside of your application, which can be a anything from a web controller, to an API controller or a CLI application. In terms of hexagonal architecture this is called a port.
Once the application receives such a command it should not care which port it came from. By handling all similar commands in a single spot (a command handler) it does not have to worry about the origins of the command.
So to give you a short overview:
[Web request] [CLI command] <-- these are ports
\ /
\ /
\ /
[Command] <--- this is a method call to your service
|
|
|
[Command handler] <--- this is the service doing the actual work
Updated my answer
Based on the code you provided I implemented what I mentioned above like so:
app/Console/Command/BotScrapeCommand.php
This is the CLI command I mentioned above. All this class has to do is:
1. Gather input arguments; (website_id) in this case
2. Wrap those arguments in a command
3. Fire off the command using the command handler
namespace App\Console\Commands;
use App\Command\ScrapePortalSiteCommand;
use CommandHandler\ScrapePortalSiteCommandHandler;
class BotScrapeCommand extends Command
{
protected $signature = 'bot:scrape {website_id}';
protected $description = 'target a portal site and scrape';
public function handle(ScrapePortalSiteCommandHandler $handler)
{
$portalSiteId = $this->argument("website_id");
$command = new ScrapePortalSiteCommand($portalSiteId);
$handler->handle($command);
}
}
app/Command/ScapePortalSiteCommand.php
This is the Command I mentioned above. Its job is to wrap all input arguments in a class, which can be used by a command handler.
namespace App\Command;
class ScrapePortalSiteCommand
{
/**
* #var int
*/
private $portalSiteId;
public function __construct(int $portalSiteId)
{
$this->portalSiteId = $portalSiteId;
}
public function getPortalSiteId(): int
{
return $this->portalSiteId;
}
}
app/CommandHandler/ScrapePortalSiteCommandHandler.php
The command handler should implement logic based on its command. In this case that's figuring out which crawler to pick, then fire that one off.
namespace App\CommandHandler;
use App\Command\ScrapePortalSiteCommand;
use App\Crawler\PortalSite1Crawler;
use App\Crawler\PortalSiteCrawlerInterface;
use InvalidArgumentException;
class ScrapePortalSiteCommandHandler
{
public function handle(ScrapePortalSiteCommand $command): void
{
$crawler = $this->getCrawlerForPortalSite($command->getPortalSiteId());
$crawler->crawl();
}
private function getCrawlerForPortalSite(int $portalSiteId): PortalSiteCrawlerInterface {
switch ($portalSiteId) {
case 1:
return new PortalSite1Crawler();
default:
throw new InvalidArgumentException(
sprintf('No crawler configured for portal site with id "%s"', $portalSiteId)
);
}
}
}
app/Crawler/PortalSiteCrawlerInterface.php
This interface is there to make sure all crawlers can be called in similar fashion. Additionally it makes for nice type hinting.
namespace App\Crawler;
interface PortalSiteCrawlerInterface
{
public function crawl(): void;
}
app/Crawler/PortalSite1Crawler.php
This is where the implementation of the actual scraping goes.
namespace App\Crawler;
class PortalSite1Crawler implements PortalSiteCrawlerInterface
{
public function crawl(): void
{
// Crawl your site here
}
}
Another update
As you had some additional questions I've updated my answer once more.
:void
The use of : void in a method declaration means the method will not return anything. In a same way public function getPortalSiteId(): int means this method will always return an integer. The use of return typehints was added to PHP 7 and is not specific to Laravel. More information on return typehints can be found in the PHP documentation.
Commands and handlers
The use of commands and command handlers is a best practice which is part of the command bus pattern. This pattern describes an universal way of dealing with user input (a command). This post offers a nice explanation on commands and handlers. Additionally, this blog post describes in more details what a command bus is, how it's used and what the advantages are. Please note that in the code I've provided the bus implementation itself is skipped. In my opinion you do not need it per se, but in some cases it does add value.

How to get Unit component? Can i use addAsync for get solution?

I want to built a program which is get problems and I found that my first batches of tests involving custom components would tend to follow code:
import mx.core.Application;   
 import mx.events.FlexEvent;
 import flexunit.framework.TestCase;
public class CustomComponentTest extends TestCase {
private var component:CustomComponent;
public function testSomeAspect() : void {
component = new CustomComponent();
component.addEventListener(FlexEvent.CREATION_COMPLETE,
addAsync(verifySomeAspect, 5000));
component.height = 0;
component.width = 0;
Application.application.addChild(component);
}
public function verifySomeAspect(event:FlexEvent) : void {}
override public function tearDown() : void {
 try {
if (component) {
Application.application.removeChild(component);
component = null;
}
} catch (e:Error) {
 }
}
First, you need to make sure the component has been fully initialized before you can reliably verify anything about it, and in Flex this happens asynchronously after it has been added to the display list. So you need to setup a callback (using FlexUnit's addAsync function) to be notified when that's happened.
Lately i've been just manually calling the methods that the runtime would call for you in the necessary places, so now my tests tend to look more like this:
import flexunit.framework.TestCase;
public class CustomComponentTest extends TestCase {
public function testSomeAspect() : void {
var component:CustomComponent = new CustomComponent();
 component.initialize();
component.validateProperties();
}
This is much easier to follow, but it kinda feels like I'm cheating a little either way. The first case is slamming it into the current Application (which would be the unit test runner shell app), and the latter isn't a "real" environment.I was wondering how other people would handle this sort of situation?
I can agree that the second version is shorter, but I'm not sure that I think it's easier to follow. The test does a lot of things that you wouldn't normally do, whereas the first example is more true to how you would use the component outside the test environment.
Also, in the second form you have to make sure that you do exactly what the framework would do, miss one step and your test isn't relevant, and each test must repeat this code. Seems to me it's better to test it in a situation that is as close to the real thing as possible.
You could have a look at dpUint's sequences, they made component testing a little more declarative:
public function testLogin():void {
var passThroughData:Object = new Object();
passThroughData.username = "myuser1";
passThroughData.password = "somepsswd";
var sequence:SequenceRunner = new SequenceRunner(this);
sequence.addStep(new SequenceSetter(form.usernameTI,
{text:passThroughData.username}));
sequence.addStep(new SequenceWaiter(form.usernameTI,
FlexEvent.VALUE_COMMIT, 100));
sequence.addStep(new SequenceSetter(form.passwordTI,
{text:passThroughData.password}));
sequence.addStep(new SequenceWaiter(form.passwordTI, FlexEvent.VALUE_COMMIT, 100));
sequence.addStep(new SequenceEventDispatcher(form.loginBtn,
new MouseEvent("click", true, false)));
sequence.addStep(new SequenceWaiter(form, "loginRequested", 100));
sequence.addAssertHandler(handleLoginEvent, passThroughData);
sequence.run();}

Selenium RC User Defined Functions

Trying to do something simple -
I have a set of statements to clear browser cookies:
public void clearCookies () {
selenium.open("http://www.myurl.com");
selenium.waitForPageToLoad("10000");
selenium.deleteAllVisibleCookies();
}
Now, if I use this function in a test script (using TestNG), calls to this work perfectly. However, if I moved this function to a separate class and change the declaration to include "static", the "selenium" keyword is not recognized.
In a configuration class (say configClass),
public static void clearCookies () {
selenium.open("http://www.myurl.com");
selenium.waitForPageToLoad("30000");
selenium.deleteAllVisibleCookies();
}
Now, in my test script, if I call configClass.clearCookies();, I get a runtime error
I tried declaring DefaultSelenium selenium = new DefaultSelenium(null);, in the clearCookies() function, but that too results in a runtime error.
I do have the import com.thoughtworks.selenium.*; import in my configClass.
Any pointers would be appreciated. Thanks.
You can do two things.
Refer to the same selenium object in both the classes i.e. in configClass and the class you are calling configClass.clearCookies().
or else
send selenium object to the clearCookies. So the code would be like this
public static void clearCookies (DefaultSelenium selenium) {
selenium.open("http://www.myurl.com");
selenium.waitForPageToLoad("30000");
selenium.deleteAllVisibleCookies();
}

Resources