Đơn vị Kiểm tra một khung trạng thái như Phaser?


9

TL; DR Tôi cần trợ giúp trong việc xác định các kỹ thuật để đơn giản hóa kiểm tra đơn vị tự động khi làm việc trong khuôn khổ trạng thái.


Lý lịch:

Tôi hiện đang viết một trò chơi trong TypeScript và khung Phaser . Phaser mô tả chính nó như một khung trò chơi HTML5 cố gắng ít nhất có thể để hạn chế cấu trúc mã của bạn. Điều này đi kèm với một vài sự đánh đổi, cụ thể là tồn tại Phaser.Game-object cho phép bạn truy cập mọi thứ: bộ nhớ cache, vật lý, trạng thái trò chơi, v.v.

Tính trạng thái này làm cho việc kiểm tra rất nhiều chức năng, chẳng hạn như Tilemap của tôi. Hãy xem một ví dụ:

Ở đây tôi đang kiểm tra xem các lớp gạch của mình có chính xác hay không và tôi có thể xác định các bức tường và sinh vật trong Tilemap của mình:

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]
            ]);

Bất kể tôi làm gì, ngay khi tôi cố gắng tạo bản đồ, Phaser sẽ gọi bộ nhớ cache trong bộ nhớ cache, nó chỉ được điền trong thời gian chạy.

Tôi không thể gọi thử nghiệm này mà không tải toàn bộ trò chơi.

Một giải pháp phức tạp có thể là viết Adaptor hoặc Proxy chỉ xây dựng bản đồ khi chúng ta cần hiển thị trên màn hình. Hoặc tôi có thể tự điền vào trò chơi bằng cách chỉ tải thủ công các tài sản tôi cần và sau đó chỉ sử dụng nó cho lớp thử nghiệm hoặc mô-đun cụ thể.

Tôi đã chọn những gì tôi cảm thấy là một giải pháp thực tế hơn, nhưng nước ngoài cho điều này. Giữa việc tải trò chơi của tôi và chơi trò chơi thực tế, tôi đã bắt đầu một cuộc TestStatechạy thử nghiệm với tất cả các tài sản và dữ liệu được lưu trong bộ nhớ cache.

Điều này thật tuyệt, bởi vì tôi có thể kiểm tra tất cả các chức năng mà tôi muốn, nhưng cũng không phải vì nó là một bài kiểm tra tích hợp và người ta tự hỏi liệu tôi không thể chỉ nhìn vào màn hình và xem liệu kẻ thù có được hiển thị hay không. Trên thực tế, không, chúng có thể đã được xác định nhầm là Vật phẩm (đã xảy ra một lần rồi) hoặc - sau đó trong các thử nghiệm - chúng có thể không được đưa ra các sự kiện gắn liền với cái chết của chúng.

Câu hỏi của tôi - Là mờ trong trạng thái thử nghiệm như thế này phổ biến? Có cách tiếp cận nào tốt hơn, đặc biệt là trong môi trường JavaScript mà tôi không biết?


Một vi dụ khac:

Được rồi, đây là một ví dụ cụ thể hơn để giúp giải thích những gì đang xảy ra:

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);
    }

Tôi xây dựng Tilemap của mình từ ba phần:

  • Bản đồ key
  • Bản đồ manifestchi tiết tất cả các tài sản (gạch lát và spritesheets) theo yêu cầu của bản đồ
  • Một mapDefinitionmô tả cấu trúc và các lớp của tilemap.

Đầu tiên, tôi phải gọi siêu để xây dựng Tilemap trong Phaser. Đây là phần gọi tất cả các lệnh gọi tới bộ đệm khi nó cố gắng tra cứu các tài sản thực tế và không chỉ các khóa được xác định trong manifest.

Thứ hai, tôi liên kết các ô xếp và các lớp gạch với Tilemap. Bây giờ nó có thể hiển thị bản đồ.

Thứ ba, tôi lặp qua lớp của tôi và tìm thấy bất kỳ đối tượng đặc biệt mà tôi muốn extrude từ bản đồ: Creatures, Items, Interactablesvà vân vân. Tôi tạo và lưu trữ các đối tượng này để sử dụng sau.

Hiện tại tôi vẫn có một API tương đối đơn giản cho phép tôi tìm, xóa, cập nhật các thực thể này:

    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 });
    }

Đây là chức năng tôi muốn kiểm tra. Nếu tôi không thêm Lớp Ngói hoặc Ngói, bản đồ sẽ không hiển thị, nhưng tôi có thể kiểm tra nó. Tuy nhiên, ngay cả việc gọi siêu (...) cũng gọi logic cụ thể theo ngữ cảnh hoặc trạng thái mà tôi không thể tách rời trong các thử nghiệm của mình.


2
Tôi bối rối. Bạn đang thử kiểm tra xem Phaser có đang thực hiện công việc tải tilemap hay bạn đang thử kiểm tra nội dung của chính tilemap? Nếu đó là trước đây, bạn thường không kiểm tra rằng phụ thuộc của bạn làm công việc của họ; đó là công việc của người bảo trì thư viện. Nếu sau này, logic trò chơi của bạn được kết hợp quá chặt chẽ với khung. Hiệu suất sẽ cho phép nhiều, bạn muốn giữ cho hoạt động bên trong của trò chơi của bạn thuần túy và để các hiệu ứng phụ cho các lớp trên cùng của chương trình để tránh loại lộn xộn này.
Doval

Không, tôi đang thử nghiệm chức năng của riêng tôi. Tôi xin lỗi nếu các bài kiểm tra không giống như vậy, nhưng có một chút gì đó nằm dưới vỏ bọc. Về cơ bản, tôi đang xem qua tilemap và khám phá các ô đặc biệt mà tôi chuyển đổi thành các thực thể trò chơi như Vật phẩm, Sinh vật, v.v. Logic này là tất cả của tôi và chắc chắn phải được kiểm tra.
IAE

1
Bạn có thể giải thích chính xác Phaser tham gia vào việc này như thế nào không? Tôi không rõ Phaser được gọi ở đâu và tại sao. Bản đồ đến từ đâu?
Doval

Tôi xin lỗi vì sự nhầm lẫn! Tôi đã thêm mã Tilemap của mình làm ví dụ về một đơn vị chức năng tôi đang cố kiểm tra. Tilemap là một tiện ích mở rộng (hoặc tùy chọn có-a) Phaser.Tilemap cho phép tôi hiển thị tilemap với một loạt các chức năng bổ sung mà tôi muốn sử dụng. Đoạn cuối nhấn mạnh lý do tại sao tôi không thể kiểm tra nó một cách cô lập. Ngay cả với tư cách là một thành phần, khoảnh khắc tôi chỉ new Tilemap(...)Phaser bắt đầu đào sâu vào bộ đệm của nó. Tôi phải trì hoãn điều đó, nhưng điều đó có nghĩa là Tilemap của tôi ở hai trạng thái, một trạng thái không thể hiển thị chính xác và trạng thái được xây dựng đầy đủ.
IAE

Dường như với tôi, như tôi đã nói trong bình luận đầu tiên của tôi, logic trò chơi của bạn quá gắn liền với khuôn khổ. Bạn sẽ có thể chạy logic trò chơi của bạn mà không cần đưa vào khuôn khổ nào cả. Việc ghép bản đồ ô vuông với các tài sản được sử dụng để vẽ nó trên màn hình đang cản trở bạn.
Doval

Câu trả lời:


2

Không biết Phaser hay Formscipt, tôi vẫn cố gắng đưa ra câu trả lời cho bạn, bởi vì những vấn đề bạn đang gặp phải là những vấn đề cũng có thể nhìn thấy với rất nhiều khung công tác khác. Vấn đề là các thành phần phải được liên kết chặt chẽ (mọi thứ đều hướng đến đối tượng Thiên Chúa và đối tượng Thiên Chúa sở hữu mọi thứ ...). Đây là điều không thể xảy ra nếu những người tạo ra khung công tác tự tạo các bài kiểm tra đơn vị.

Về cơ bản, bạn có bốn tùy chọn:

  1. Dừng thử nghiệm đơn vị.
    Tùy chọn này không nên được chọn, trừ khi tất cả các tùy chọn khác không thành công.
  2. Chọn một khung khác hoặc viết của riêng bạn.
    Chọn một khung công tác khác đang sử dụng thử nghiệm đơn vị và mất khớp nối, sẽ giúp cuộc sống dễ dàng hơn rất nhiều. Nhưng có lẽ không có cái nào bạn thích và do đó bạn bị mắc kẹt với khuôn khổ mà bạn có bây giờ. Viết của riêng bạn có thể mất rất nhiều thời gian.
  3. Đóng góp vào khuôn khổ và làm cho nó kiểm tra thân thiện.
    Có lẽ là dễ nhất để làm, nhưng nó thực sự phụ thuộc vào thời gian bạn có và mức độ sẵn sàng của những người tạo ra khung để chấp nhận các yêu cầu kéo.
  4. Bọc khung.
    Tùy chọn này có lẽ là tùy chọn tốt nhất để bắt đầu với thử nghiệm đơn vị. Bọc các đối tượng nhất định mà bạn thực sự cần trong bài kiểm tra đơn vị và tạo các đối tượng giả cho phần còn lại.

2

Giống như David, tôi không quen thuộc với Phaser hoặc Typecript, nhưng tôi nhận ra mối quan tâm của bạn là phổ biến đối với thử nghiệm đơn vị với các khung và thư viện.

Câu trả lời ngắn gọn là có, làm mờ là cách chính xác và phổ biến để xử lý việc này với kiểm tra đơn vị . Tôi nghĩ rằng ngắt kết nối là hiểu sự khác biệt giữa thử nghiệm đơn vị bị cô lập và thử nghiệm chức năng.

Kiểm thử đơn vị chứng minh rằng các phần nhỏ trong mã của bạn tạo ra kết quả chính xác. Mục tiêu của kiểm tra đơn vị không bao gồm kiểm tra mã bên thứ 3. Giả định là mã đã được thử nghiệm để hoạt động như mong đợi của bên thứ 3. Khi viết một bài kiểm tra đơn vị cho mã dựa trên khung, việc sử dụng một số phụ thuộc nhất định để chuẩn bị một trạng thái cụ thể cho mã hoặc hoàn toàn làm mờ khung / thư viện. Một ví dụ đơn giản là quản lý phiên cho một trang web: có thể shim luôn trả về trạng thái nhất quán, hợp lệ thay vì đọc từ bộ lưu trữ. Một ví dụ phổ biến khác là làm mờ dữ liệu trong bộ nhớ và bỏ qua bất kỳ thư viện nào sẽ truy vấn cơ sở dữ liệu, vì mục tiêu không phải là kiểm tra cơ sở dữ liệu hoặc thư viện mà bạn đang sử dụng để kết nối với nó, chỉ là mã của bạn xử lý dữ liệu chính xác.

Nhưng kiểm tra đơn vị tốt không có nghĩa là người dùng cuối sẽ thấy chính xác những gì bạn mong đợi. Kiểm thử chức năng có nhiều quan điểm cấp cao hơn rằng toàn bộ tính năng đang hoạt động, khung và tất cả. Quay lại ví dụ về một trang web đơn giản, một bài kiểm tra chức năng có thể đưa ra yêu cầu web về mã của bạn và kiểm tra phản hồi để có kết quả hợp lệ. Nó trải dài trên tất cả các mã cần thiết để tạo ra kết quả. Bài kiểm tra dành cho chức năng nhiều hơn là tính chính xác của mã.

Vì vậy, tôi nghĩ rằng bạn đang đi đúng hướng với thử nghiệm đơn vị. Để thêm kiểm tra chức năng cho toàn bộ hệ thống, tôi sẽ tạo các thử nghiệm riêng biệt gọi thời gian chạy Phaser và kiểm tra kết quả.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.