Anzeige

Getting your hands dirty testing Magento 2 (at MageTitansIT)

vinaikopp
5. Feb 2016
Anzeige

Más contenido relacionado

Anzeige

Getting your hands dirty testing Magento 2 (at MageTitansIT)

  1. GETTING YOUR HANDS DIRTY TESTING MAGENTO 2 Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  2. ME Freelance Developer & Trainer https://github.com/vinai https://twitter.com/vinaikopp Dev since 1998 PHP WebDev since PHP3 (1999) Magento since 2008-01-01 Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  3. TESTING Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  4. WHO IS DOING IT? Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  5. WHAT TOOL? > PHPUnit? > Behat && PHPSpec? > Selenium? > Codeception? Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  6. BUZZWORD BINGO Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  7. BUZZWORD I: UNIT TEST Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  8. $ vendor/bin/phpunit -c "dev/tests/unit/phpunit.xml.dist" Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  9. $ alias unit-test="$(pwd)/vendor/bin/phpunit -c $(pwd)/dev/tests/unit/phpunit.xml.dist" Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  10. $ unit-test Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  11. $ unit-test "app/code/My/Module/Test/Unit" Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  12. TRY IT vendor/bin/phpunit -c "dev/tests/unit/phpunit.xml.dist" vendor/magento/module-catalog/Test/Unit/Block Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  13. BUZZWORD II: INTEGRATION TEST Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  14. WHAT IS THE DIFFERENCE? Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  15. REAL OBJECTS INTERACT Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  16. INTEGRATION TESTS NEED THE RUNTIME ENVIRONMENT Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  17. A failing unit test tells me exactly where my code is broken. Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  18. A failing integration test only tells me that something is broken. Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  19. Separate DB dev/tests/integration/etc/install-config-mysql.php.dist Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  20. $ vendor/bin/phpunit -c dev/tests/integration/phpunit.xml Could not read "dev/tests/integration/phpunit.xml". Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  21. $ cd dev/tests/integration $ ../../../vendor/bin/phpunit Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  22. Every run gets it's own etc, pub and var dirs dev/tests/integration/tmp/ sandbox-0-b79c13eb842cf3211459b11c775bfbde/ Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  23. BUZZWORD III: TDD Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  24. WHAT IS TDD? Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  25. THE 3 RULES Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  26. I. No production code without a failing unit test Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  27. II. Once there is a failing unit test, stop and make it pass by writing production code Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  28. III. Once the test passes, stop writing production code until you have a failing unit test again Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  29. TDD FOR MAGENTO 2 Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  30. NO DIFFERENCE Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  31. </TDD> Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  32. To learn more about TDD, check out the Clean Coders Videos from Robert C. Martin http://cleancoders.com (or do a training) Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  33. INTEGRATION TESTS FOR MAGENTO 2 Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  34. VERY SPECIFIC Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  35. PLATFORM KNOWLEDGE Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  36. Magento 2 Testing Framework dev/tests/integration/framework Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  37. Test Framework ObjectManager MagentoTestFrameworkObjectManager::getInstance() Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  38. Dependencies $configReader = $objectManager->create(Reader::class); $deploymentConfig = $objectManager->create( DeploymentConfig::class, ['reader' => $configReader] ); Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  39. BUZZWORD IV FIXTURES Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  40. Fix the system into a known state before a test is executed. Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  41. Simple PHP Scripts Relative to dev/tests/integration/testsuite Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  42. /** * @magentoDataFixture Magento/Sales/_files/order.php * @magentoDataFixture Magento/Customer/_files/customer.php */ public function testSomethingWithAnOrderAndACustomer() { // ... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  43. CONFIG FIXTURES Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  44. Store Scope /** * @magentoConfigFixture current_store catalog/price/scope 1 */ public function testDoesSomeoneReadThisHelpMeLetMeOut() { // ... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  45. Global Scope /** * @magentoConfigFixture currency/options/allow USD */ public function testHowMashAndPieTastes() { // ... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  46. BUZZWORD V TEST ISOLATION Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  47. FRESH INSTALL FOR EACH TEST RUN Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  48. dev/tests/integration/phpunit.xml.dist <phpunit> ... <php> ... <const name="TESTS_CLEANUP" value="enabled"/> ... </php> ... </phpunit> Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  49. TESTS_CLEANUP == disabled SPEEDS THINGS UP Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  50. TESTS_CLEANUP == disabled MANUAL CLEANUP REQUIRED Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  51. integration/tmp/sandbox-* Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  52. DATABASE ISOLATION Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  53. TRANSACTIONS OR ROLLBACK SCRIPTS Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  54. TRANSACTIONS /** * @magentoDbIsolation enabled */ public function testWithoutSideEffects { // ... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  55. TRANSACTIONS /** * @magentoDataFixture Magento/Foo/_files/bar.php */ public function testTheFixtureIsAppliedWithinATransaction { // ... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  56. ROLLBACK SCRIPTS /** * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDbIsolation disabled */ public function testTransactionalCode() { // ... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  57. Before Test include 'Magento/Customer/_files/customer.php'; Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  58. After Test include 'Magento/Customer/_files/customer_rollback.php'; Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  59. APPLICATION ISOLATION Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  60. REINSTANTIATE ALMOST EVERYTHING Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  61. RESET MAGENTO AFTER TEST /** * @magentoAppIsolation enabled */ public function testTestWithSingletons() { Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  62. Controller Tests: Automatic reset class FooBarIndexActionTest extends MagentoTestFrameworkTestCaseAbstractController { public function testTheLayoutIsRendered() { $this->dispatch('foo/bar/index'); Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  63. REMEMBER THIS Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  64. ISOLATE YOUR TESTS Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  65. AND ALL WILL BE GOOD Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  66. [breathing pause] Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  67. APPLICATION AREA Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  68. APP AREAS global frontend adminhtml Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  69. /** * @magentoAppArea frontend */ class AddressesTest extends PHPUnit_Framework_TestCase { Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  70. MORE AREAS Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  71. webapi_rest webapi_soap cron Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  72. MagentoTestFrameworkAppState Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  73. public function testPluginInWebapiRestScope() { $class = MagentoTestFrameworkAppState::class; $appAreaState = $this->objectManager->get($class); $appAreaState->setAreaCode('webapi_rest'); Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  74. protected function tearDown() { // ... $appAreaState->setAreaCode(null); } Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  75. ACTION [CONTROLLER] TESTS Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  76. FRONTEND MagentoTestFrameworkTestCase AbstractController ADMINHTML MagentoTestFrameworkTestCase AbstractBackendController Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  77. EXAMPLE! Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  78. Event Observer for: sales_order_place_after Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  79. I. TESTING THE CONFIGURATION Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  80. public function testTheModuleRegistersASalesOrderPlaceAfterObserver() { $class = MagentoFrameworkEventConfig::class; $eventConfig = ObjectManager::getInstance()->create($class); $observers = $eventConfig->getObservers('sales_order_place_after'); $this->assertArrayHasKey('meetup_observer', $observers); $expectedClass = MeetupObserverObserverSalesOrderPlaceAfter::class; $this->assertSame($expectedClass, $observers['meetup_observer']['instance']); $this->assertFalse($observers['meetup_observer']['shared']); } Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  81. 1/2) Get the config in array format $class = MagentoFrameworkEventConfig::class; $eventConfig = ObjectManager::getInstance()->create($class); $event = 'sales_order_place_after'; $observers = $eventConfig->getObservers($event); Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  82. 2/2) Assert config values $this->assertArrayHasKey('meetup_observer', $observers); $this->assertSame( SalesOrderPlaceAfter::class, $observers['meetup_observer']['instance'] ); $this->assertFalse($observers['meetup_observer']['shared']); Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  83. II. TESTING THE OBSERVER Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  84. O_0(NOTHING TO TEST) Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  85. III. INTEGRATION TEST Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  86. Our observer has no side effects. We have to improvise... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  87. /** * @magentoDataFixture Magento/Sales/_files/order.php */ public function testSalesOrderPlaceAfterObserverIsCalled() { $this->injectMockObserverWithExpectation(); $order = $this->getOrderFixture(); $this->objectManager ->create(OrderManagementInterface::class) ->place($order); } Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  88. public function injectMockObserverWithExpectation() { $mockObserver = $this->getMock(SalesOrderPlaceAfter::class); $mockObserver->expects($this->once())->method('execute'); $this->registerObserverTestDouble($mockObserver); } Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  89. private function registerObserverTestDouble($mockObserver) { $class = MagentoFrameworkEventConfigData::class; $eventConfigData = $this->objectManager->get($class); $event = 'sales_order_place_after'; $name = 'meetup_observer'; $eventConfigData->merge( [$event => [$name => ['shared' => true]]] ); // ... } Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  90. private function registerObserverTestDouble($mockObserver) { // ... $this->objectManager->addSharedInstance( $mockObserver, SalesOrderPlaceAfter::class ); } Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  91. /** * @magentoDataFixture Magento/Sales/_files/order.php */ public function testSalesOrderPlaceAfterObserverIsCalled() { // ... $order = $this->getOrderFixture(); // ... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  92. private function getOrderFixture() { $class = OrderRepositoryInterface::class; $orderRepository = $this->objectManager->create($class); $criteria = $this->objectManager ->create(SearchCriteriaBuilder::class) ->addFilter('increment_id', '100000001') ->create(); $result = $orderRepository->getList($criteria); return array_values($result->getItems())[0]; } Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  93. /** * @magentoDataFixture Magento/Sales/_files/order.php */ public function testSalesOrderPlaceAfterObserverIsCalled() { $this->injectMockObserverWithExpectation(); $order = $this->getOrderFixture(); $this->objectManager ->create(OrderManagementInterface::class) ->place($order); } Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  94. GO FORTH AND TESTALL THINGS! Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  95. ONE MORE THING... Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  96. MAGE2KATAS Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  97. MAGE2KATAS > Screencast Series > Examples > Code Katas mage2katas.com Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
  98. THANK YOU <3 Getting your hands dirty Testing Magento 2 - Magento London Meetup, Feb. 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
Anzeige