I would like to see the results of individual unit tests in the browser. I would like to do something like...
public function test() {
$test = new \Tests\Unit\ExampleTest();
dd($test->testBasicTest());
}
That just returns null. I also tried exec('vendor/bin/phpunit'); but would like to stay away from exec() (and to mention, that snippet creates an endless loop for some reason).
What if you try doing a echo of your dd?
public function test() {
$test = new \Tests\Unit\ExampleTest();
echo(dd($test->testBasicTest()));
}
Related
It is not quite clear to me when I should use integrations and unit test
If I want to test the following code without making any http requests, should I use an integration test or unit ?
When a thread is created, the activity is recorded and an email event is fired
ThreadObserver extends Observer
{
public function created(Thread $thread)
{
Activity::record($thead);
event(new ThreadWasCreated($thread);
}
}
class RecordsActivityTest
{
public function it_records_the_acitivty_when_a_thread_is_created()
{
Thread::factory()->create();
$this->assertDatabaseHas('activities', […]);
}
}
Another example, I want to test that give a query it returns the expected results .
class Search
{
public function __construct(protected Index $index)
{
}
public function handle($query)
{
$this->index->search($query);
// more code
}
}
class SearchTest
{
public function it_returns_the_expected_result()
{
$search = new Search(new Index);
$results = $search->handle(“some query”);
$this->assertEquals(“some
Data”);
}
}
Do I need a unit test, to test the Search class or an integration test because it is dependent on another class ?
The first example, I'd most likely write an integration test, but instead of testing if the database has a value, I'd just mock the event https://laravel.com/docs/8.x/mocking#event-fake and assert that the event was dispatched.
The second example however, it could go either way. You can write a unit test and just mock everything OR, you could write an integration test so you can use the Laravel helpers, specifically the https://laravel.com/docs/8.x/mocking#http-fake if you're testing an API, otherwise mockery if you're testing and SDK for example.
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.
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']);
}
I am fairly new to Test Driven Development and I just started learning the SOLID principles so I was hoping someone could help me out. I'm having some conceptual trouble understanding the Single Responsibility Principle in the context of developing unit tests and methods in the TDD paradigm. For instance, say I want to develop a method that deletes an item from a database- before my code would have looked like the following...
I'd start with defining a test case:
"Delete_Item_ReturnsTrue": function() {
//Setup
var ItemToDelete = "NameOfSomeItem";
//Action
var BooleanResult = Delete( ItemToDelete );
//Assert
if ( BooleanResult === true ) {
return true;
} else {
console.log("Test: Delete_Item_ReturnsTrue() - Failed.");
return false;
}
}
I'd run the test to make sure it failed, then I'd develop the method...
function Delete( ItemToDelete ) {
var Database = ConnectToDatabase();
var Query = BuildQuery( ItemToDelete );
var QueryResult = Database.Query( Query );
if ( QueryResult.error !== true ) {
//if there's no error then...
return true;
} else {
return false;
}
}
If I'm understanding the Single Responsibility Principle correctly, the method that I originally wrote had the responsibilities of deleting the item AND returning true if there wasn't an error. So if I follow the SOLID paradigm the original method should be refactored to look something like...
function Delete( WhatToDelete, WhereToDeleteItFrom ) {
WhereToDeleteItFrom.delete( WhatToDelete );
}
Doing the design as such, changed my method from a boolean function to a void function so now I can't really test the new method in the same manner I was testing my old method.
I guess I could test for thrown exceptions but then doesn't that make it an integration test rather than a unit test because there's no actual exception thrown in the method?
Do I design and implement an extra function which checks the database for use in my unit test?
Do I just not test it because it's void? How exactly does that work in TDD?
Do I pass in a mock repository and then return it after it's state has changed? Doesn't that just essientially bring me back to square one?
Do I pass in a reference to a mock repository and then just test against the repository after the method completes? Wouldn't that be considered a side effect though?
So the single responsibility principle says: They should be exact one reason when I need to change a function.
So let's look at your function:
function Delete( ItemToDelete ) {
var Database = ConnectToDatabase();
var Query = BuildQuery( ItemToDelete );
var QueryResult = Database.Query( Query );
if ( QueryResult.error !== true ) {
//if there's no error then...
return true;
} else {
return false;
}
}
Database changes: ConnectToDatabese() needs a change, but not this function
Changes in the db structure: BuildQuery() needs to be changed (maybe)
...
So on the first look, your function looks good. The naming on some places is a little bit confusing. A function should be start with a small letter. For example "connectToDatabase()". It is a little bit surprising that connectToDatabase returns an Object.
The name BuildQuery seems to be wrong, because BuildQuery( myItem ) returns a query wich is deleting something.
But I would never have such a long complicated function which just one testcase.
You need to write more test cases.
For the first test case you could write the function like that:
function Delete( ItemToDelete) {
return true
}
The next testcase could be "call the buildDeleteQuery function with the item id". At that point you need to think about how you call your db. If you have a dbObject, which does that you could mock that Object.
function Delete( ItemToDelete ) {
buildDeleteQuery( ItemToDelete.id )
return true
}
Now more and more test cases. Remember that there will be also test cases for buildDeleteQuery. But for the outer function you could mock buildDeleteQuery.
Now to answer some of your questions:
When you first write the test and then write the code, you will have testable code.
That code looks different, because the tests should be not to complicated. When you want easy tests you will automaticly have more smaller functions.
Yes, you will write a test for every function you call.
Maybe you will mock objects. And if you are not able to test your db call without a wrapping function you will create a function, which allows you to test that functionality.
Remember your tests are the documentation of your code. So keep them simple as possible.
But the most important thing: Keep practicing! When you start with TDD it takes some time. A good and fun resource is: Uncle Bobs Bowling Kata Just download the slides from the website and look how tdd is done step by step.
I have a unit test that works just fine when it is hitting a real object that hits teh real data storage. Something like that:
[TestMethod]
public void ATest()
{
var p = new Provider();
var data = p.GetData();
...
}
This test gets executed in all modes, gets the data back and does everything that is exected from it. Now, say I want to mock the provider using Rhino Mocks. Provider class implements IProvider. So I go and write something like this:
[TestMethod]
public void ATest()
{
var p = MockRepository.GenerateStub<IProvider>();
...
var data = p.GetData();
...
}
But now when I try to debug this test, it doesn't work. At all. I mean, I put a breakpoint on the first line of this method (on the '{' itself) and it is not being hit. Kind of weird...
I am all new to Rhino Mocks, maybe I am missing something obvious?
You didn't define a return value for the GetData call on the mock. Try something like this:
p.Stub(s => s.GetData()).Return(testData);