Tutorial: Testing and TDD - Spryker Commerce OS

Edit on GitHub

This tutorial is also available on the Spryker Training website. For more information and hands-on exercises, visit the Spryker Training website.

This document helps you understand the main concepts of testing with Spryker and see how simple it is to build tests. You will use the Test-Driven Development (TDD) approach.

Spryker’s testing structure and data handling make it very easy to develop using TDD. You will build a simple module that reverses a string and test it.

Using TDD, you will write the test first, see it fails, and then write the string reverser that makes the test pass.

1. Build the test that fails

As everything in Spryker is modular, tests are also modular. To build a new test, you simply add a new module inside your tests.

Spryker introduces a new namespace for testing in your project called PyzTest.

As you are going to work with Zed, the test module is for Zed:

  1. Create a new test module inside the tests directory in you project tests/PyzTest/Zed and call it StringReverser.
  2. Spryker uses Codeception as a testing framework. In tests/PyzTest/Zed/StringReverser, using Codeception, add the config file for your new module and call it codeception.yml. The config looks like this:

Code sample

namespace: PyzTest\Zed\StringReverser

paths:
    tests: .
    data: _data
    support: _support
    log: _output

coverage:
    enabled: true
    remote: false
    whitelist: { include: ['../../../../src/*'] }

suites:
    Business:
        path: Business
        class_name: StringReverserBusinessTester
        modules:
            enabled:
                - Asserts
                - \PyzTest\Shared\Testify\Helper\Environment
                - \SprykerTest\Shared\Testify\Helper\LocatorHelper:
                    projectNamespaces: ['Pyz']
  1. Add modules Config and DependencyProvider:
namespace Pyz\Zed\StringReverser;

use Spryker\Zed\Kernel\AbstractBundleConfig;

class StringReverserConfig extends AbstractBundleConfig
{

}


namespace Pyz\Zed\StringReverser;

use Spryker\Zed\Kernel\AbstractBundleDependencyProvider;

class StringReverserDependencyProvider extends AbstractBundleDependencyProvider
{

}
  1. Add the Business folder inside tests/PyzTest/Zed/StringReverser.
  2. From Codeception, generate the needed test classes:
vendor/bin/codecept build -c tests/PyzTest/Zed/StringReverser
  1. Create a facade test class to add your test inside it. The facade test class looks like this:

Code sample

namespace PyzTest\Zed\StringReverser\Business;

use Codeception\Test\Unit;

/**
 * @group PyzTest
 * @group Zed
 * @group StringReverser
 * @group Business
 * @group Facade
 * @group StringReverserFacadeTest
 * Add your own group annotations below this line
 */
class StringReverserFacadeTest extends Unit
{
	/**
	 * @var \PyzTest\Zed\StringReverser\StringReverserBusinessTester
	 */
	protected $tester;
}
  1. Spryker can generate transfer objects for testing using a concept called Data Builders*. Data Builders generators work similarly to transfer generators, except that they use data fakers to generate random data for testing purposes. You can generate Data Builders using the same transfer object schemas and running the command console transfer:databuilder:generate.

To add the data faker rules for the test, create a data builder schema. Inside tests/, create a new directory called _data. Then, add the data builder schema inside the directory and call it string_reverser.databuilder.xml.

The schema looks very similar to a transfer object schema. This schema only adds the rules when generating the data builders.

You can generate the data builders without the rules and without the schema.

Code sample

<?xml version="1.0"?>
	<transfers
		xmlns="spryker:transfer-01"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="spryker:transfer-01 http://static.spryker.com/transfer-01.xsd"
	>

	<transfer name="StringReverser">
		<property name="originalString" dataBuilderRule="realText(20, 2)"/>
		<property name="reversedString" dataBuilderRule="realText(20, 2)"/>
        </transfer>

    </transfers>
  1. Data builders return transfer objects of the same type of the data builder. You need to have a transfer object called StringReverser so that the data builder can work.

Data builders cannot even be generated if the transfer object is not there. In src/Pyz/Shared/StringReverser/Transfer, add the StringReverser transfer.

Code sample

<?xml version="1.0"?>
<transfers xmlns="spryker:transfer-01"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="spryker:transfer-01 http://static.spryker.com/transfer-01.xsd">

	<transfer name="StringReverser">
		<property name="originalString" type="string"/>
		<property name="reversedString" type="string"/>
	</transfer>
</transfers>
  1. Generate the transfer object first:
console transfer:generate
  1. Generate the data builder:
console transfer:databuilder:generate

You must have both of them generated.

  1. Add the test method. Test if the string is reversed correctly.

A test in Spryker consists of three main blocks:

  • Arrange—to prepare the test data.
  • Act—to act on the data.

In the case described in this tutorial, Act calls the facade method.

  • Assert—to check the results.

Code sample

/**
 * @return void
 */
public function testStringIsReversedCorrectly(): void
{
	// Arrange
	$stringReverserTransfer = (new StringReverserBuilder([
		'originalString' => 'Hello Spryker!'
	]))->build();

	// Act
	$stringReverserFacade = $this->tester->getLocator()->stringReverser()->facade();
	$stringReverserResultTransfer = $stringReverserFacade->reverseString($stringReverserTransfer);

	// Assert
	$this->assertEquals(
		'!rekyrpS olleH',
		$stringReverserResultTransfer->getReversedString()
	);
}
  1. Run the test using the command vendor/bin/codecept run -c tests/PyzTest/Zed/StringReverser.

The test at this point must fail and give an error that the StringReverserFacade cannot be resolved because it does not exist.

2. Make the test pass

Write the actual logic (feature) to reverse a string and make the test pass:

  1. In Zed, add a new module called StringReverser.
  2. Add the facade and the needed logic to reverse the string in a model. Your Zed module must have StringReverserConfig and StringReverserDependencyProvider so that the class locator can work with your test.

Use the code generators to generate the module in Zed console code:generate:module:zed StringReverser.

Code samples
namespace Pyz\Zed\StringReverser\Business;

use Generated\Shared\Transfer\StringReverserTransfer;
use Spryker\Zed\Kernel\Business\AbstractFacade;

/**
 * @method \Pyz\Zed\StringReverser\Business\StringReverserBusinessFactory getFactory()
 */
class StringReverserFacade extends AbstractFacade implements StringReverserFacadeInterface
{
    /**
     * @param \Generated\Shared\Transfer\StringReverserTransfer $stringReverserTransfer
     *
     * @return \Generated\Shared\Transfer\StringReverserTransfer
     */
    public function reverseString(StringReverserTransfer $stringReverserTransfer): StringReverserTransfer
    {
        return $this->getFactory()
            ->createStringReverser()
            ->reverse($stringReverserTransfer);
    }
}
namespace Pyz\Zed\StringReverser\Business;

use Pyz\Zed\StringReverser\Business\Reverser\StringReverser;
use Pyz\Zed\StringReverser\Business\Reverser\StringReverserInterface;
use Spryker\Zed\Kernel\Business\AbstractBusinessFactory;

class StringReverserBusinessFactory extends AbstractBusinessFactory
{
    /**
     * @return \Pyz\Zed\StringReverser\Business\Reverser\StringReverserInterface
     */
    public function createStringReverser(): StringReverserInterface
    {
        return new StringReverser();
    }
}
namespace Pyz\Zed\StringReverser\Business\Reverser;

use Generated\Shared\Transfer\StringReverserTransfer;

class StringReverser implements StringReverserInterface
{
    /**
     * @param \Generated\Shared\Transfer\StringReverserTransfer $stringReverserTransfer
     *
     * @return \Generated\Shared\Transfer\StringReverserTransfer
     */
    public function reverse(StringReverserTransfer $stringReverserTransfer): StringReverserTransfer
    {
        $reversedString = strrev($stringReverserTransfer->getOriginalString());
        $stringReverserTransfer->setReversedString($reversedString);

        return $stringReverserTransfer;
    }
}
  1. Run the test again
vendor/bin/codecept run -c tests/PyzTest/Zed/StringReverser

The test must pass.