Magento 2 Fixtures by Fabian Schmengler
🧙🏻‍♂ https://tddwizard.com/
An alternative to the procedural script based fixtures in Magento 2 integration tests.
It aims to be:
- extensible
- expressive
- easy to use
Install it into your Magento 2 project with composer:
composer require --dev tddwizard/magento2-fixtures
- Magento 2.3 or Magento 2.4
- PHP 7.3 or 7.4 (7.1 and 7.2 is allowed via composer for full Magento 2.3 compatibility but not tested anymore)
If you need a customer without specific data, this is all:
protected function setUp(): void
{
$this->customerFixture = new CustomerFixture(
CustomerBuilder::aCustomer()->build()
);
}
protected function tearDown(): void
{
$this->customerFixture->rollback();
}
It uses default sample data and a random email address. If you need the ID or email address in the tests, the CustomerFixture
gives you access:
$this->customerFixture->getId();
$this->customerFixture->getEmail();
You can configure the builder with attributes:
CustomerBuilder::aCustomer()
->withEmail('[email protected]')
->withCustomAttributes(
[
'my_custom_attribute' => 42
]
)
->build()
You can add addresses to the customer:
CustomerBuilder::aCustomer()
->withAddresses(
AddressBuilder::anAddress()->asDefaultBilling(),
AddressBuilder::anAddress()->asDefaultShipping(),
AddressBuilder::anAddress()
)
->build()
Or just one:
CustomerBuilder::aCustomer()
->withAddresses(
AddressBuilder::anAddress()->asDefaultBilling()->asDefaultShipping()
)
->build()
The CustomerFixture
also has a shortcut to create a customer session:
$this->customerFixture->login();
Similar to the customer builder you can also configure the address builder with custom attributes:
AddressBuilder::anAddress()
->withCountryId('DE')
->withCity('Aachen')
->withPostcode('52078')
->withCustomAttributes(
[
'my_custom_attribute' => 42
]
)
->asDefaultShipping()
Product fixtures work similar as customer fixtures:
protected function setUp(): void
{
$this->productFixture = new ProductFixture(
ProductBuilder::aSimpleProduct()
->withPrice(10)
->withCustomAttributes(
[
'my_custom_attribute' => 42
]
)
->build()
);
}
protected function tearDown(): void
{
$this->productFixture->rollback();
}
The SKU is randomly generated and can be accessed through ProductFixture
, just as the ID:
$this->productFixture->getSku();
$this->productFixture->getId();
To create a quote, use the CartBuilder
together with product fixtures:
$cart = CartBuilder::forCurrentSession()
->withSimpleProduct(
$productFixture1->getSku()
)
->withSimpleProduct(
$productFixture2->getSku(), 10 // optional qty parameter
)
->build()
$quote = $cart->getQuote();
Checkout is supported for logged in customers. To create an order, you can simulate the checkout as follows, given a customer fixture with default shipping and billing addresses and a product fixture:
$customerFixture = new CustomerFixture(CustomerBuilder::aCustomer()->withAddresses(
AddressBuilder::anAddress()->asDefaultBilling(),
AddressBuilder::anAddress()->asDefaultShipping()
)->build());
$customerFixture->login();
$checkout = CustomerCheckout::fromCart(
CartBuilder::forCurrentSession()
->withProductRequest(ProductBuilder::aVirtualProduct()->build()->getSku())
->build()
);
$order = $checkout->placeOrder();
It will try to select the default addresses and the first available shipping and payment methods.
You can also select them explicitly:
$order = $checkout
->withShippingMethodCode('freeshipping_freeshipping')
->withPaymentMethodCode('checkmo')
->withCustomerBillingAddressId($this->customerFixture->getOtherAddressId())
->withCustomerShippingAddressId($this->customerFixture->getOtherAddressId())
->placeOrder();
The OrderBuilder
is a shortcut for checkout simulation.
$order = OrderBuilder::anOrder()->build();
Logged-in customer, products, and cart item quantities will be generated internally unless more control is desired:
$order = OrderBuilder::anOrder()
->withProducts(
// prepare catalog product fixtures
ProductBuilder::aSimpleProduct()->withSku('foo'),
ProductBuilder::aSimpleProduct()->withSku('bar')
)->withCart(
// define cart item quantities
CartBuilder::forCurrentSession()->withSimpleProduct('foo', 2)->withSimpleProduct('bar', 3)
)->build();
Orders can be fully or partially shipped, optionally with tracks.
$order = OrderBuilder::anOrder()->build();
// ship everything
$shipment = ShipmentBuilder::forOrder($order)->build();
// ship only given order items, add tracks
$shipment = ShipmentBuilder::forOrder($order)
->withItem($fooItemId, $fooQtyToShip)
->withItem($barItemId, $barQtyToShip)
->withTrackingNumbers('123-FOO', '456-BAR')
->build();
Orders can be fully or partially invoiced.
$order = OrderBuilder::anOrder()->build();
// invoice everything
$invoice = InvoiceBuilder::forOrder($order)->build();
// invoice only given order items
$invoice = InvoiceBuilder::forOrder($order)
->withItem($fooItemId, $fooQtyToInvoice)
->withItem($barItemId, $barQtyToInvoice)
->build();
Credit memos can be created for either all or some of the items ordered. An invoice to refund will be created internally.
$order = OrderBuilder::anOrder()->build();
// refund everything
$creditmemo = CreditmemoBuilder::forOrder($order)->build();
// refund only given order items
$creditmemo = CreditmemoBuilder::forOrder($order)
->withItem($fooItemId, $fooQtyToRefund)
->withItem($barItemId, $barQtyToRefund)
->build();
To manage multiple fixtures, fixture pools have been introduced for all entities:
Usage demonstrated with the ProductFixturePool
:
protected function setUp()
{
$this->productFixtures = new ProductFixturePool;
}
protected function tearDown()
{
$this->productFixtures->rollback();
}
public function testSomethingWithMultipleProducts()
{
$this->productFixtures->add(ProductBuilder::aSimpleProduct()->build());
$this->productFixtures->add(ProductBuilder::aSimpleProduct()->build(), 'foo');
$this->productFixtures->add(ProductBuilder::aSimpleProduct()->build());
$this->productFixtures->get(); // returns ProductFixture object for last added product
$this->productFixtures->get('foo'); // returns ProductFixture object for product added with specific key 'foo'
$this->productFixtures->get(0); // returns ProductFixture object for first product added without specific key (numeric array index)
}
With the config fixture you can set a configuration value globally, i.e. it will ensure that it is not only set in the default scope but also in all store scopes:
ConfigFixture::setGlobal('general/store_information/name', 'Ye Olde Wizard Shop');
It uses MutableScopeConfigInterface
, so the configuration is not persisted in the database. Use @magentoAppIsolation enabled
in your test to make sure that changes are reverted in subsequent tests.
You can also set configuration values explicitly for stores with ConfigFixture::setForStore()
The MIT License (MIT). Please see License File for more information.