Etsy sync WordPress plugin 13

Mocking a user shops response with Codeception modules while developing Etsy Little Helper.

The problem

I need to test the code parsing Etsy API responses in a variety of situations and before a single line of that code is written I need a mock server in place.
My luck is in the possibility Codeception offers to create modules to extend its functionalities and I’ve created the first method I need to simulate different responses when requesting for a user shops.

Template

To properly simulate the response I’ve transformed the one I’ve posted on my earlier article to be used by Smarty; a template engine is my go-to solution in this cases and Smarty allows for quite some flexibility.
Without too much complication I’ve transformed the JSON response to default all of its values but the one I will surely need: a list of shop_ids to populate it.


    {
    "count": {$shopIds|@count},
    "results": [
        {foreach $shopIds as $shopId}
            {
            "shop_id": {$shopId},
            "shop_name": "{$shopName|default:'MyEtsyShop'}",
            "user_id": {$userId|default:222222},
            "creation_tsz": 1393407522,
            "title": "{$title|default:'My etsy shop'}",
            "announcement": "{$announcement|default:'Crypto-tattoo tank-traps market table convenience store render-farm.'}",
            "currency_code": "{$currencyCode|default:'$'}",
            "is_vacation": {$isVacation|default:'false'},
            "vacation_message": "{$vacationMessage|default:'Boat silent shanty town wristwatch ablative car nodality Tokyo. Concrete bridge hacker human youtube rebar RAF computer boy savant face forwards. Corrupted human claymore mine Shibuya monofilament tube city 8-bit futurity woman dolphin.'}",
            "sale_message": "{$saleMessage|default:'Tube knife convenience store engine corporation free-market crypto-Chiba tanto computer nano-wonton soup sunglasses. Military-grade office otaku urban realism assault uplink grenade sprawl BASE jump motion man Chiba render-farm. Shibuya into systema dead vehicle modem courier car vinyl pen alcohol San Francisco sprawl disposable woman. Nodal point girl RAF papier-mache tower rain smart-augmented reality order-flow dolphin otaku vinyl artisanal. Post-shoes futurity grenade euro-pop pistol artisanal kanji alcohol DIY tank-traps math-concrete Chiba.'}",
            "digital_sale_message": "{$digitalSaleMessage|default:'Sub-orbital grenade realism man post-face forwards garage table pistol shrine. Tower paranoid free-market soul-delay BASE jump range-rover A.I. RAF nodality corporation j-pop katana advert ablative rain car sign. Legba BASE jump assassin savant artisanal sign nodal point digital nodality faded meta-j-pop RAF computer engine convenience store systema.'}",
            "last_updated_tsz": 1427289394,
            "listing_active_count": {$listingActiveCount|default:23},
            "login_name": "{$loginName|default:'johndoe'}",
            "accepts_custom_requests": {$acceptsCustomRequests|default:'false'},
            "policy_welcome": "{$policyWelcome|default:'Math-BASE jump receding soul-delay tanto shrine Shibuya. Market hacker sunglasses film face forwards 8-bit table kanji assassin katana construct augmented reality monofilament. Shoes sprawl skyscraper knife network youtube man Shibuya city cartel cardboard. Free-market shrine towards 3D-printed euro-pop nodal point sub-orbital monofilament convenience store singularity motion car Kowloon augmented reality. A.I. hacker market free-market crypto-lights urban franchise fetishism cyber-hotdog neon film youtube.'}",
            "policy_shipping": "{$policyShipping|default:'Bridge 8-bit soul-delay disposable dolphin convenience store fetishism shoes towards sunglasses girl camera saturation point vehicle courier.'}",
            "policy_refunds": "{$policyRefund|default:'Nodality singularity shoes wristwatch savant ablative marketing 3D-printed convenience store corporation woman car artisanal RAF free-market physical.'}",
            "policy_additional": {$policyAdditional|default:'null'},
            "policy_seller_info": {$policySellerInfo|default:'null'},
            "policy_updated_tsz": 1396363691,
            "vacation_autoreply": {$vacationAutoreply|default:'null'},
            "url": "https:\/\/www.etsy.com\/shop\/{$shopName|default:'MyEtsyShop'}?utm_source=etsylittlehelper&utm_medium=api&utm_campaign=api",
            "image_url_760x100": "https:\/\/img0.etsystatic.com\/033\/0\/{$shopId}\/iusb_760x100.13409064_z8j1.jpg",
            "num_favorers": {$numFavorers|default:200},
            "languages": ["en-US"],
            "upcoming_local_event_id": null
            }
            {if $shopIds|@count gt 1},{/if}
        {/foreach}
    ],
    "params": {
        "user_id": "{$loginName|default:'johndoe'}",
        "limit": 25,
        "offset": 0,
        "page": null
    },
    "type": "Shop",
    "pagination": {
        "effective_limit": 25,
        "effective_offset": 0,
        "next_offset": null,
        "effective_page": 1,
        "next_page": null
    }
    }

That template will be fed to an helper class, a Codeception module, I’ve called EtsyApi (file is /tests/_support/EtsyApi.php)


    namespace Helper;

    use Codeception\Module;

    class EtsyApi extends Module {

        /**
         * @var Smarty
         */
        private $smarty;

        public function getUserShopsResponse( array $shopIds, array $customData = [ ] ) {
            $smarty = $this->getSmarty();
            $smarty->assign( 'shopIds', $shopIds );
            foreach ( $customData as $key => $value ) {
                $smarty->assign( $key, $value );
            }

            return $smarty->fetch( $this->getShopTemplate() );
        }

        private function getShopTemplate() {
            return dirname( __FILE__ ) . '/templates/shopResponse.tpl';
        }

        private function getSmarty() {
            if ( empty( $this->smarty ) ) {
                $this->smarty = new \Smarty();
            }

            return $this->smarty;
        }
    }

I just need to add that to the modules my UnitTester class should use (file is tests/unit_suite.yml)


    # Codeception Test Suite Configuration

    # suite for unit (internal) tests.
    class_name: UnitTester
    modules:
        enabled: [Asserts, \Helper\Unit, \Helper\EtsyApi]

run the command that will tell Codeception to rebuild the helpers


    codeception build

and create a first test for a still not-existing class that will use that method


    codecept generate:test ELH_UserShopsParser

in that I can finally run a test to prove that the response mocker works


    class ELH_ShopsParserTest extends \Codeception\TestCase\Test {

        /**
         * @var \UnitTester
         */
        protected $tester;

        protected function _before() {
        }

        protected function _after() {
        }

        // tests
        public function testMockUserShopResponse() {
            $shop_id  = '1111111';
            $userShopsResponse = $this->tester->getUserShopsResponse( [ $shop_id ] );
            $response = json_decode( $userShopsResponse );

            $this->assertTrue( $response->results[0]->shop_id == $shop_id );
        }

    }

and get a green test result. A mock response green test