Laravel 5.4; How to run unit tests from a Laravel package? - unit-testing

I am trying to develop a Laravel composer package and run unit tests from within it. After spending the last couple of days reading various outdated and contradictory guides and blogposts, I am completely confused as to how to go about this.
Here's what I know so far:
I shouldn't run tests from the main Laravel installation. Tests should all be contained within the package. I'll admit this has a certain logic to it.
There's something called Orchestra Testbench. If you're developing a Laravel package then apparently you should use this.
There's also something called Laravel Dusk, which is included in Laravel 5.4.
I can get Orchestra Test Bench working with some basic tests from the examples given, but I don't really understand what's going on and the documentation explains almost nothing. When it comes to testing my application's routes, I can't get anything to work.
I don't understand if Orchestra and Dusk can play together, or if I have to choose between them. If so, which one should I use?
And if it's Laravel Dusk I should be using, then how do I run it from within my package directory?

I agree with:
I shouldn't run tests from the main Laravel installation. Tests should all be contained within the package
To avoid installing Laravel in your application and bootstraping it by hand to test your package, you can use Orchestral Testbench.
The docs of Orchestral are pretty good today, but I will give anyway a short example how to use it, if you want to make unit tests on some Eloquent models.
First, make sure that your package has a ServiceProvider class which points to your migration files as described in the docs.
Then, require the orchestral/testbench package into your package and create a file in tests/TestCase.php.
The file TestCase should do the following:
Extend Orchestra TestCase
Point to your Service-Provider with getPackageProviders
Setup a SQLite database in memory with getEnvironmentSetUp
Run migrate to create you tables in setUp()
Here is an example:
<?php
namespace MyVendorName\MyPackageName\Tests;
use MyVendorName\MyPackageName\MyServiceProvider;
class TestCase extends \Orchestra\Testbench\TestCase
{
public function setUp(): void
{
parent::setUp();
$this->artisan('migrate', ['--database' => 'testbench'])->run();
}
/**
* add the package provider
*
* #param $app
* #return array
*/
protected function getPackageProviders($app)
{
return [MyServiceProvider::class];
}
/**
* Define environment setup.
*
* #param \Illuminate\Foundation\Application $app
* #return void
*/
protected function getEnvironmentSetUp($app)
{
// Setup default database to use sqlite :memory:
$app['config']->set('database.default', 'testbench');
$app['config']->set('database.connections.testbench', [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
]);
}
}
And to answer your second question: Laravel Dusk works well with Orchestral Testbench. Similar as above where you needed to mock up a database to test your Eloquent package models, you have to mock up routes & views with Orchestral to make the Browser tests with Laravel Dusk.
For more details, check out my blog post.

Here is what I do when developing a package:
1) Create a folder named src/tests.
2) Inside this folder I follow the "normal" structure:
A Unit folder for unit tests.
A Feature folder.
A Browser folder for running tests with Laravel Dusk (for the end-to-end tests).
Note: Dusk allows you to test your application as a real user would do clicking links, filling forms, etc. It is really cool to test an application user interface (even if it uses JavaScript). So, if your package does not include any interface you may not need Dusk.
3) All the tests use the base testing suite of Laravel, so you need a full Laravel installation with Dusk included (Dusk only needed if you are using browser tests).
So for the Unit and Feature tests I extended \Tests\TestCase.
Browser tests extend \Tests\DuskTestCase.
4) Now run your tests using the path to your tests:
phpunit /path/to/packages/pascall/icms
or, for Dusk:
php artisan dusk /path/to/packages/pascall/icms
(or you can cd /path/to/packages/pascall/icms and then run phpunit.
I have never used Orchestra because my team finds this option more straightforward and easier so I can't help you with that.
Drawbacks:
Dusk test failures will appear in the Default Laravel Dusk screenshot folder (for us this is fine so we have not spent time investingating this).

I did all of the things that others said but my problem didn't been solved unless I change <testsuites> in phpunit.xml of Laravel's root :
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">packages\YourVendorName\YourPackageName</directory>
</testsuite>
</testsuites>

Related

Run all tests except for a JUnit Category in IntelliJ

I pretty much only use JUnit Categories for non-unit tests that I don't want to run as part of the test suite. In NUnit I could use Explicit, but the only thing I've found comparable in JUnit is a Category. In gradle, it's simple to exclude a Category, but I can't figure out how to do this with IntelliJ's test runner. I see how to run tests that belong to a Category, but not how to exclude them.
Some time has passed since I asked this question, and in that time the Gradle test runner has become the default (at least for me). So though the built-in runner may not have this functionality, you can easily create a gradle task that excludes categories or tags:
test {
useJUnitPlatform {
excludeTags 'integrationTest'
excludeTags 'endToEndTest'
excludeTags 'testDriver'
}
options {
exclude '**/*Integration*'
exclude 'integrationTest'
exclude 'endToEndTest'
exclude 'testDriver'
}
}

How to test React Native classes that do not have a render method?

I am building a mobile application with React Native and ran into a problem when trying to unit test my code. Currently I do have both Jest and Enzyme being used in the testing.
How would I be able to test the methods in a certain class/file that does not contain a render() method and does not "extend as a Component"? This class is used to query information from an API and saving it into variables.
The documentation about Jest and Enzyme at
https://facebook.github.io/jest/docs/api.html#content
http://airbnb.io/enzyme/docs/api/index.html
seem to be focused on testing rendered components.
Assuming you've got a module export set up, you actually just import the object/class and then write a test against it using a jasmine syntax. You can try it out live right here:
https://facebook.github.io/jest/docs/getting-started.html#content
If you notice on the "add-test.js" file, the first line is a require to get your object under test. In this case:
const add = require('./add');
Then it's just a matter of regular old jasmine-style testing.

How to run a single test or single TestCase with django-nose?

With Django's normal test runner, you can drill down to run tests in a specific app, a specific subclass of TestCase, or a specific test within a specific subclass of TestCase.
E.g.:
./manage.py test myapp.MyTestCase.test_something
However, django-nose doesn't appear to support anything beyond testing a specific app. How do I replicate the last two behaviors?
Nose supports the following syntax (note : between test script name and test class name):
./manage.py test myapp.tests.test_script:MyTestCase.test_method
The correct answer is ./manage.py test myapp/tests/test_script:MyTestCase.test_method.
Using dots in the relative path did not work for me, but slashes did.

Invoking django tests in subdirectories

I recently split up an app into subdirectories. For example, I had a "shop" app, and I split it up into shop/foo, shop/bar, shop/baz subdirectories, treating each one as a separate app, so my INSTALLED_APPS now looks like:
"shop",
"shop.foo",
"shop.bar",
"shop.baz",
...
I want to be able to run the tests in shop/foo/tests.py by doing:
python manage.py test shop.foo
However, if I do that, I get the error:
ValueError: Test label 'shop.foo' does not refer to a test
On the other hand, I can run the tests by doing this:
python manage.py test foo
Why is this happening, and what can I change so that I can run the tests as "shop.foo" instead of "foo"?
This is because Django expects the arguments to test command to be of the format:
app_label[.TestCase[.test_method]]
There is no way of doing this with the stock test runner (see, Carl Meyers comment). If everything goes well, this should be fixed in Django 1.5, but in the meantime you can use an alternate runner which accepts full module paths: django-discovery-runner.
django-discover-runner has been made part of Django 1.6.. :)
For Version <1.6 , it can be used as a third party app.

Custom test suite for django app

I have a pretty complex django app which has following structure.
/myapp
/myapp/obj1/..
/myapp/obj1/views.py
/myapp/obj1/forms.py
/myapp/obj2/..
/myapp/obj2/views.py
/myapp/obj2/forms.py
/myapp/tests/..
/myapp/tests/__init__.py
/myapp/tests/test_obj1.py
/myapp/tests/test_obj2.py
I have a lot more objects. In /myapp/tests/__init__.py I import TestCase instances from test_obj1.py and test_obj2.py and it is enough to run all available test.
What I'm trying to do is to create a custom test suite. According to the documentation:
There is a second way to define the test suite for a module: if you
define a function called suite() in either models.py or tests.py, the
Django test runner will use that function to construct the test suite
for that module. This follows the suggested organization for unit
tests. See the Python documentation for more details on how to
construct a complex test suite.
So, i've created this function like this:
def suite():
suite = unittest.TestSuite()
suite.addTest(TestObj1Form())
suite.addTest(TestObj2Form())
return suite
However, when I run tests I get this error: ValueError: no such test method in <class 'myproject.myapp.tests.test_obj1.TestObj1Form'>: runTest. Of course I can define this method, but then if I run test it will invoke only this method and ignore all of the test* methods.
Any suggestions how to create a custom test suite for django app properly? I've googled and I found nothing about that.
You should add all your tests with a special function:
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestObj1Form))