After this simple example, let's write another test that illustrates a more complex scenario. And we will write one that tests the CsvImporter plugin we created in the previous chapter.
There is quite a lot of functionality that goes into this plugin and working with it—we have the actual importing, the plugin and configuration entity creation, the user interface for doing so, and so on. And it's a very good example of functionality that can benefit from a multi-methodology test coverage. And in this respect, we start with testing its underlying purpose, that of the product import, for which we don't need browser interactions. This means that we can use a Kernel test.
Similar to how we wrote the previous test, we can start with the class like so (this time in the products module):
namespace Drupal\Tests\products\Kernel; use Drupal\KernelTests\KernelTestBase; /** * Tests the CSV Product Importer * * @group products */ class CsvImporterTest extends KernelTestBase {}
Nothing new so far.
Next, we need to specify the modules we need loaded. And here we have a bigger list:
/** * Modules to enable. * * @var array */ protected static $modules = ['system', 'csv_importer_test', 'products', 'image', 'file', 'user'];
Only the products module may seem obvious to you at this point, but all the rest are also needed. The system, image, file and user modules are all somehow needed for dealing with the file upload and storage process that is needed for the CsvImporter plugin.
But you may be wondering what's with the csv_importer_test module there. Oftentimes, you may need to create modules used only for the tests—usually because they contain some configuration you want to use in your testing. In our case, we did so to demonstrate where these modules would go and to add a products.csv test file that we can use in our tests.
Tests modules go inside the tests/modules folder of the module that contains the tests that use them. So, in our case, we have csv_importer_test with its info.yml file:
name: CSV Importer Test description: Used for testing the CSV Importer core: 8.x type: module package: Testing
And the mentioned CSV file we will use is right next to it:
id,name,number 1,Car,45345 2,Motorbike,54534
Now that we covered that, we can write the test method:
/** * Tests the import of the CSV based plugin. */ public function testImport() { $this->installEntitySchema('product'); $this->installEntitySchema('file'); $this->installSchema('file', 'file_usage'); $manager = $this->container->get('entity_type.manager'); $products = $manager->getStorage('product')->loadMultiple(); $this->assertEmpty($products); $csv_path = drupal_get_path('module', 'csv_importer_test') . '/products.csv'; $csv_contents = file_get_contents($csv_path); $file = file_save_data($csv_contents, 'public://simpletest-products.csv', FileSystemInterface::EXISTS_REPLACE); $config = $manager->getStorage('importer')->create([ 'id' => 'csv', 'label' => 'CSV', 'plugin' => 'csv', 'plugin_configuration' => [ 'file' => [$file->id()] ], 'source' => 'Testing', 'bundle' => 'goods', 'update_existing' => true ]); $config->save(); $plugin = $this->container->get('products.importer_manager')->createInstanceFromConfig('csv'); $plugin->import(); $products = $manager->getStorage('product')->loadMultiple(); $this->assertCount(2, $products); $products = $manager->getStorage('product')->loadByProperties(['number' => 45345]); $this->assertNotEmpty($products); $this->assertCount(1, $products); }
And the use statement at the top:
use Drupal\Core\File\FileSystemInterface;
The initial setup here is a bit more complicated, partly because of Kernel tests not installing module schemas. Using the parent installEntitySchema(), method we can install all the necessary tables for the Product and File content entities. However, since we are working with managed files, we also need to install the file_usage table manually. It is not technically an entity table. Again, there is no shame in arriving at these steps using trial and error.
Now that we have the basics set up, we do a sanity check and ensure that we don't have any product entities in the database. There is no reason why we should have any, but it doesn't hurt to ensure it. This guarantees a valid test since our goal will be to later assert the existence of products.
Then we create a managed File entity by using the products.csv file from the csv_importer_test module. The drupal_get_path() function is a very common way of retrieving the relative path to a module or a theme, regardless of where it is actually located. And we save the contents of this file into the public:// filesystem of the testing environment. Keep in mind, though, that after the test runs successfully, this file gets removed as Drupal cleans up after itself.
Next, we need to create an Importer configuration entity that uses the CSV-based plugin to run the import. And instead of doing it through the UI, we do it programmatically. Using the storage manager, we create the entity as we learned in Chapter 6, Data Modeling and Storage. Once we have that, we use the Importer plugin manager to create an instance based on this configuration entity (to which we gave the ID csv). And finally, we run the import of the products.
Now, for the assertions, we do a double check. Since our test CSV contains two rows, we load all the product entities again and assert that we have a total of two. No more, no less. And here we see another useful assertion method for working with arrays: assertCount(). But then we get a bit more specific and try to load a product that has a field value (the number) equal to an expected number from the test CSV file. And assert that it is, in fact, found as well.
We could even do some more assertions. For example, we can check that all the Product field values have been set correctly. I'll let you explore ways in which you can do this—either by querying based on these values or asserting equality between field values and their expected ones. But it's important to not go overboard as it will impact speed and, in some cases, add insufficient value to the test coverage to compensate for it. The trick is to find the right balance.
Finally, with our test in place, we can actually run it:
../vendor/bin/phpunit ../modules/custom/products/tests/src/Kernel/CsvImporterTest.php
And this test should pass as well.