इकाई फेजर जैसे स्टेटफुल फ्रेमवर्क का परीक्षण?


9

टीएल; डीआर मुझे एक स्टेटफुल फ्रेमवर्क के भीतर काम करते समय स्वचालित इकाई परीक्षण को सरल बनाने के लिए तकनीकों की पहचान करने में मदद की आवश्यकता है।


पृष्ठभूमि:

मैं वर्तमान में टाइपस्क्रिप्ट और फेजर फ्रेमवर्क में एक खेल लिख रहा हूं । फेजर खुद को HTML5 गेम फ्रेमवर्क के रूप में वर्णित करता है जो आपके कोड की संरचना को प्रतिबंधित करने के लिए जितना संभव हो उतना कम प्रयास करता है। यह कुछ ट्रेड-ऑफ्स के साथ आता है, अर्थात् एक ईश्वर-वस्तु Phaser.Game मौजूद है जो आपको सब कुछ एक्सेस करने देता है: कैश, भौतिकी, गेम स्टेट्स, और बहुत कुछ।

यह स्थितित्मकता बहुत सारी कार्यक्षमता का परीक्षण करना कठिन बनाती है, जैसे कि मेरा तिलमप। आइए एक उदाहरण देखें:

यहाँ मैं परीक्षण कर रहा हूँ कि मेरी टाइल की परतें सही हैं या नहीं और मैं अपने तिलमप के भीतर की दीवारों और प्राणियों की पहचान कर सकता हूँ:

export class TilemapTest extends tsUnit.TestClass {
    constructor() {
        super();

        this.map = this.mapLoader.load("maze", this.manifest, this.mazeMapDefinition);

        this.parameterizeUnitTest(this.isWall,
            [
                [{ x: 0, y: 0 }, true],
                [{ x: 1, y: 1 }, false],
                [{ x: 1, y: 0 }, true],
                [{ x: 0, y: 1 }, true],
                [{ x: 2, y: 0 }, false],
                [{ x: 1, y: 3 }, false],
                [{ x: 6, y: 3 }, false]
            ]);

        this.parameterizeUnitTest(this.isCreature,
            [
                [{ x: 0, y: 0 }, false],
                [{ x: 2, y: 0 }, false],
                [{ x: 1, y: 3 }, true],
                [{ x: 4, y: 1 }, false],
                [{ x: 8, y: 1 }, true],
                [{ x: 11, y: 2 }, false],
                [{ x: 6, y: 3 }, false]
            ]);

कोई फर्क नहीं पड़ता कि मैं क्या करता हूं, जैसे ही मैं नक्शा बनाने की कोशिश करता हूं, फेजर आंतरिक रूप से इसे कैश कहता है, जो केवल रनटाइम के दौरान आबादी है।

मैं पूरे खेल को लोड किए बिना इस परीक्षा का आह्वान नहीं कर सकता।

एक जटिल समाधान एक एडेप्टर या प्रॉक्सी लिखना हो सकता है जो केवल मानचित्र बनाता है जब हमें इसे स्क्रीन पर प्रदर्शित करने की आवश्यकता होती है। या मैं खुद को केवल उन परिसंपत्तियों को लोड करके खेल को आबाद कर सकता हूं जिनकी मुझे जरूरत है और फिर केवल विशिष्ट परीक्षण वर्ग या मॉड्यूल के लिए इसका उपयोग कर रहा हूं।

मैंने चुना कि मुझे जो लगता है वह अधिक व्यावहारिक है, लेकिन इसका विदेशी समाधान है। मेरे खेल की लोडिंग और उसके वास्तविक खेल के बीच, मैंने इसमें एक शर्म की बात TestStateहै कि सभी परिसंपत्तियों और पहले से लोड किए गए डेटा के साथ परीक्षण चलाता है।

यह अच्छा है, क्योंकि मैं अपनी सभी कार्यक्षमता का परीक्षण कर सकता हूं, लेकिन यह भी अनकूल है, क्योंकि यह तकनीकी एकीकरण परीक्षण है और एक आश्चर्य है कि क्या मैं सिर्फ स्क्रीन पर नहीं देख सकता हूं और देख सकता हूं कि क्या दुश्मन प्रदर्शित होते हैं। वास्तव में, नहीं, उन्हें एक आइटम के रूप में गलत पहचान दिया गया था (एक बार पहले ही हो चुका है) या बाद में परीक्षणों में- उन्हें शायद उनकी मृत्यु से जुड़ी घटनाओं को नहीं दिया गया है।

मेरा सवाल - क्या यह आम जैसी परीक्षा-अवस्था में चमक रहा है? क्या विशेष रूप से जावास्क्रिप्ट वातावरण में बेहतर दृष्टिकोण हैं, जिनके बारे में मुझे जानकारी नहीं है?


एक और उदाहरण:

ठीक है, यह बताने के लिए कि क्या हो रहा है, यह बताने के लिए एक अधिक ठोस उदाहरण है:

export class Tilemap extends Phaser.Tilemap {
    // layers is already defined in Phaser.Tilemap, so we use tilemapLayers instead.
    private tilemapLayers: TilemapLayers = {};

    // A TileMap can have any number of layers, but
    // we're only concerned about the existence of two.
    // The collidables layer has the information about where
    // a Player or Enemy can move to, and where he cannot.
    private CollidablesLayer = "Collidables";
    // Triggers are map events, anything from loading
    // an item, enemy, or object, to triggers that are activated
    // when the player moves toward it.
    private TriggersLayer    = "Triggers";

    private items: Array<Phaser.Sprite> = [];
    private creatures: Array<Phaser.Sprite> = [];
    private interactables: Array<ActivatableObject> = [];
    private triggers: Array<Trigger> = [];

    constructor(json: TilemapData) {
        // First
        super(json.game, json.key);

        // Second
        json.tilesets.forEach((tileset) => this.addTilesetImage(tileset.name, tileset.key), this);
        json.tileLayers.forEach((layer) => {
            this.tilemapLayers[layer.name] = this.createLayer(layer.name);
        }, this);

        // Third
        this.identifyTriggers();

        this.tilemapLayers[this.CollidablesLayer].resizeWorld();
        this.setCollisionBetween(1, 2, true, this.CollidablesLayer);
    }

मैं तीन भागों से अपने तिलमाप का निर्माण करता हूं:

  • मानचित्र key
  • manifestब्यौरा सभी परिसंपत्तियों (tilesheets और spritesheets) मानचित्र के लिए आवश्यक
  • एक mapDefinitionजो टिलेमैप की संरचना और परतों का वर्णन करता है।

पहले, मुझे फेजर के भीतर तिलमैप के निर्माण के लिए सुपर को कॉल करना होगा। यह वह हिस्सा है जो उन सभी कॉल्स को कैश करने के लिए आमंत्रित करता है क्योंकि यह वास्तविक परिसंपत्तियों को देखने की कोशिश करता है न कि केवल परिभाषित कीज़ में manifest

दूसरा, मैं टाइल्स और टाइल की परतों को तिलमाप के साथ जोड़ता हूं। यह अब मैप रेंडर कर सकता है।

तीसरा, मैं पुनरावृति मेरी परतों के माध्यम से और किसी भी विशेष वस्तुओं पाते हैं कि मैं नक्शे से बाहर निकालना चाहते हैं: Creatures, Items, Interactablesऔर इसके आगे। मैं बाद में उपयोग के लिए इन वस्तुओं को बनाता और संग्रहीत करता हूं।

मेरे पास वर्तमान में एक अपेक्षाकृत सरल एपीआई है जो मुझे इन संस्थाओं को खोजने, हटाने, अद्यतन करने देता है:

    wallAt(at: TileCoordinates) {
        var tile = this.getTile(at.x, at.y, this.CollidablesLayer);
        return tile && tile.index != 0;
    }

    itemAt(at: TileCoordinates) {
        return _.find(this.items, (item: Phaser.Sprite) => _.isEqual(this.toTileCoordinates(item), at));
    }

    interactableAt(at: TileCoordinates) {
        return _.find(this.interactables, (object: ActivatableObject) => _.isEqual(this.toTileCoordinates(object), at));
    }

    creatureAt(at: TileCoordinates) {
        return _.find(this.creatures, (creature: Phaser.Sprite) => _.isEqual(this.toTileCoordinates(creature), at));
    }

    triggerAt(at: TileCoordinates) {
        return _.find(this.triggers, (trigger: Trigger) => _.isEqual(this.toTileCoordinates(trigger), at));
    }

    getTrigger(name: string) {
        return _.find(this.triggers, { name: name });
    }

यह कार्यक्षमता है जिसे मैं जांचना चाहता हूं। यदि मैं टाइल परत या टाइलसेट नहीं जोड़ता हूं, तो नक्शा प्रस्तुत नहीं होगा, लेकिन मैं इसका परीक्षण करने में सक्षम हो सकता हूं। हालाँकि, यहां तक ​​कि सुपर (...) को कॉल करना संदर्भ-विशिष्ट या स्टेटफुल लॉजिक को संदर्भित करता है जिसे मैं अपने परीक्षणों में अलग नहीं कर सकता।


2
मैं उलझन में हूं। क्या आप यह जांचने की कोशिश कर रहे हैं कि फेजर टिलेमैप को लोड करने का काम कर रहा है या आप खुद टिलेमैप की सामग्री का परीक्षण करने की कोशिश कर रहे हैं? यदि यह पूर्व है, तो आप आमतौर पर यह परीक्षण नहीं करते हैं कि आपके आश्रित अपना काम करते हैं; वह लाइब्रेरी मेंटेनर का काम है। यदि बाद वाला, आपका गेम लॉजिक बहुत कसकर फ्रेमवर्क से जुड़ा हुआ है। जितना प्रदर्शन की अनुमति होगी, आप अपने खेल के आंतरिक कामकाज को शुद्ध रखना चाहते हैं और इस तरह की गड़बड़ से बचने के लिए कार्यक्रम की सबसे ऊपरी परतों पर दुष्प्रभाव छोड़ते हैं।
22

नहीं, मैं अपनी कार्यक्षमता का परीक्षण कर रहा हूं। मुझे खेद है कि यदि परीक्षण ऐसा नहीं दिखता है, लेकिन कवर के तहत थोड़ा सा चल रहा है। अनिवार्य रूप से, मैं तिलमप के माध्यम से देख रहा हूं और विशेष टाइलों की खोज कर रहा हूं जो कि मैं गेम एंटिटीज जैसे आइटम, क्रिएचर, और इसके बाद में परिवर्तित करता हूं। यह तर्क सब मेरा है और निश्चित रूप से परीक्षण किया जाना चाहिए।
IAE

1
क्या आप बता सकते हैं कि वास्तव में फेजर इसमें कैसे शामिल है? यह मेरे लिए स्पष्ट नहीं है कि फेजर कहां से लाया जाता है और क्यों। नक्शा कहां से आ रहा है?
डोभाल

मैं भ्रम के लिए माफी चाहता हूँ! मैंने अपना तिलमप कोड एक कार्यक्षमता की एक इकाई के उदाहरण के रूप में जोड़ा है जिसे मैं परीक्षण करने की कोशिश कर रहा हूं। तिलमैप एक एक्सटेंशन है (या वैकल्पिक रूप से एक) Phaser.Tilemap जो मुझे अतिरिक्त कार्यक्षमता का एक गुच्छा के साथ tilemap प्रदान करता है जो मैं उपयोग करना चाहता हूं। अंतिम पैराग्राफ हाइलाइट करता है कि मैं इसे अलगाव में परीक्षण क्यों नहीं कर सकता। यहां तक ​​कि एक घटक के रूप में, मैं अभी new Tilemap(...)फेजर अपने कैश में खोदना शुरू करता हूं । मुझे लगता है कि टालना होगा, लेकिन इसका मतलब है कि मेरा तिलमप दो राज्यों में है, एक जो खुद को ठीक से प्रस्तुत नहीं कर सकता है, और पूरी तरह से निर्मित है।
IAE

यह मुझे लगता है कि, जैसा कि मैंने अपनी पहली टिप्पणी में कहा था, आपका गेम लॉजिक फ्रेमवर्क से बहुत अधिक जुड़ा हुआ है। आपको फ्रेमवर्क में लाए बिना अपने गेम लॉजिक को चलाने में सक्षम होना चाहिए। टाइल के नक्शे को स्क्रीन पर खींचने के लिए उपयोग की गई संपत्ति को युग्मित करना रास्ते में मिल रहा है।
डोभाल

जवाबों:


2

फेजर या टाइपस्पर्ट को न जानते हुए, मैं अभी भी आपको उत्तर देने की कोशिश करता हूं, क्योंकि आप जिन समस्याओं का सामना कर रहे हैं, वे समस्याएं हैं जो बहुत सारे अन्य ढांचे के साथ दिखाई देती हैं। समस्या यह है कि घटकों को कसकर युग्मित किया जाता है (सब कुछ ईश्वर वस्तु को इंगित करता है, और ईश्वर वस्तु सब कुछ का मालिक है ...)। यह कुछ ऐसा है जो संभव नहीं था अगर फ्रेमवर्क के रचनाकारों ने यूनिट-टेस्ट खुद बनाया।

मूल रूप से आपके पास चार विकल्प हैं:

  1. यूनिट-परीक्षण बंद करो।
    यह विकल्प तब तक नहीं चुना जाना चाहिए, जब तक कि अन्य सभी विकल्प विफल न हों।
  2. एक और फ्रेमवर्क चुनें या अपना खुद का लिखें।
    एक और फ्रेमवर्क चुनना जो इकाई-परीक्षण का उपयोग कर रहा है और युग्मन खो गया है, जीवन को बहुत आसान बना देगा। लेकिन शायद कोई भी ऐसा नहीं है जिसे आप पसंद करते हैं और इसलिए अब आपके पास जो ढांचा है, उससे आप चिपके हुए हैं। अपना खुद का लेखन बहुत समय ले सकता है।
  3. ढांचे में योगदान करें और इसे अनुकूल बनाएं।
    संभवतः करने के लिए सबसे आसान है, लेकिन यह वास्तव में इस बात पर निर्भर करता है कि आपके पास कितना समय है और फ्रेमवर्क के रचनाकारों को पुल अनुरोधों को स्वीकार करना है।
  4. ढाँचा लपेटो।
    यह विकल्प संभवतः इकाई-परीक्षण के साथ आरंभ करने का सबसे अच्छा विकल्प है। कुछ वस्तुओं को लपेटें जो आपको यूनिट-परीक्षणों में वास्तव में चाहिए और बाकी के लिए नकली वस्तुएं बनाएं।

2

डेविड की तरह, मैं फेजर या टाइपस्क्रिप्ट से परिचित नहीं हूं, लेकिन मैं फ्रेमवर्क और लाइब्रेरी के साथ इकाई परीक्षण के लिए सामान्य होने के रूप में आपकी चिंताओं को पहचानता हूं।

संक्षिप्त उत्तर हां है, इकाई परीक्षण के साथ इसे संभालने के लिए चमकाना सही और सामान्य तरीका है । मुझे लगता है कि डिस्कनेक्ट पृथक इकाई परीक्षण और कार्यात्मक परीक्षण के बीच अंतर को समझ रहा है।

यूनिट परीक्षण साबित करता है कि आपके कोड के छोटे खंड सही परिणाम देते हैं। एक इकाई परीक्षण के लक्ष्य में तीसरे पक्ष के कोड का परीक्षण शामिल नहीं है। धारणा यह है कि 3 डी पार्टी द्वारा अपेक्षित के रूप में काम करने के लिए कोड को पहले से ही परीक्षण किया गया है। कोड के लिए एक यूनिट टेस्ट लिखते समय, जो एक फ्रेमवर्क पर निर्भर करता है, यह निश्चित निर्भरता को तैयार करने के लिए आम है कि कोड के लिए एक विशेष राज्य की तरह क्या दिखता है, या फ्रेमवर्क / लाइब्रेरी को पूरी तरह से शिम करना। एक सरल उदाहरण एक वेबसाइट के लिए सत्र प्रबंधन है: शायद शिम हमेशा भंडारण से पढ़ने के बजाय वैध, सुसंगत स्थिति देता है। एक अन्य आम उदाहरण स्मृति में डेटा को चमकाना और किसी भी लाइब्रेरी को बायपास करना है जो एक डेटाबेस को क्वेरी करेगा, क्योंकि लक्ष्य डेटाबेस या लाइब्रेरी का परीक्षण करने के लिए नहीं है जिसे आप इसे कनेक्ट करने के लिए उपयोग कर रहे हैं, बस आपका कोड डेटा को सही तरीके से संसाधित करता है।

लेकिन अच्छी इकाई परीक्षण का मतलब यह नहीं है कि अंतिम उपयोगकर्ता वही देखेगा जो आप उम्मीद करते हैं। कार्यात्मक परीक्षण एक उच्च स्तर का दृष्टिकोण लेता है जो एक पूरी सुविधा काम कर रही है, चौखटे और सभी। एक साधारण वेबसाइट के उदाहरण पर वापस, एक कार्यात्मक परीक्षण आपके कोड के लिए एक वेब अनुरोध कर सकता है और मान्य परिणामों की प्रतिक्रिया की जांच कर सकता है। यह परिणाम उत्पन्न करने के लिए आवश्यक सभी कोड में फैला हुआ है। परीक्षण विशिष्ट कोड शुद्धता के लिए कार्यक्षमता से अधिक है।

इसलिए मुझे लगता है कि आप इकाई परीक्षण के साथ सही रास्ते पर हैं। पूरे सिस्टम के कार्यात्मक परीक्षण को जोड़ने के लिए मैं अलग-अलग परीक्षण बनाऊंगा जो कि फेजर रनटाइम का आह्वान करता है और परिणामों की जांच करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.