Hikayeler

Reklam vermek için turkmmo@gmail.com

Unity UI Test Otomasyonu - Oyun arayüzünüz için testler nasıl yazılır (Rehber) | Mobil Oyun Geliştirme

sergioneral

enjoy the silence 👌
Telefon Numarası Onaylanmış Üye TC Kimlik Numarası Doğrulanmış Üye
Fahri Üye
Katılım
17 Haz 2010
Konular
3,640
Mesajlar
19,614
Online süresi
17g 50672s
Reaksiyon Skoru
4,815
Altın Konu
163
Başarım Puanı
474
Yaş
31
TM Yaşı
15 Yıl 10 Ay 10 Gün
MmoLira
-933
DevLira
0

Metin2 EP, Valorant VP dahil tüm oyun ürünlerini en uygun fiyatlarla bulabilir, Item ve Karakterlerinizi hızlıca satabilirsiniz. HEMEN TIKLA!

ui-test-cover-2xl.png

Merhaba değerli Turkmmo takipçileri, Unity Test Framework, oyununuz için otomatik testler yazmanın temel bir yolunu sunar. Unity ile basit oyun nesnelerini test etmek oldukça kolay olsa da, kullanıcı arayüzünün beklendiği gibi çalıştığından emin olmak başka bir hikaye.

Bunun nedeni, kullanıcı arayüzünün oyunun geri kalanıyla etkileşime girdiği için birim testlerinden daha fazlasını gerektirmesidir. Bunun yerine entegrasyon testleri yazmak gerekir. Bu biraz daha zor bir iştir ve bu gönderi size bunu nasıl yapacağınızı gösterecektir.

Bu eğitimde basit bir 3'lü eşleştirme oyunu alacağız ve kullanıcı arayüzünün düzgün çalıştığından emin olmak için Unity testleri yazacağız. Öğreticiyi takip etmek için Unity Test Runner'ı nasıl kullanacağınızı bildiğiniz varsayılmaktadır. Buna aşina değilseniz, Unity Test Framework'e nasıl başlayacağınızla ilgili Eğitimimize göz atmanızı öneririz.

Örnek Proje

Bu eğitim için kullanacağız. Kod > Zip İndir'e tıklayarak projeyi indirin.

2021-05-13_01-12-md.png

Unity ile projeyi açın. Scenes/Game.unity sahnesini açın.

Sahneyi başlatın ve nasıl çalıştığını anlamak için oyunu oynayın. Bu basit bir 3'lü eşleştirme oyunudur, şekerleri değiştirmek ve onları yok etmek için tıklarsınız. Sınırlı sayıda hamle vardır ve maç sayısı kadar skor artar.

Yazının geri kalanında, oyunun beklendiği gibi çalışıp çalışmadığını kontrol edebilmemiz için bir dizi entegrasyon testi yazacağız.

Oyunun başlayıp başlamadığını kontrol etme

Otomatik testlerimizin ilki, menünün oyunu başlatabilmesini sağlayacak. İşte eylem planımız:
  • Menü sahnesini yükleyin​
  • Oynat düğmesini bulun​
  • Bir tıklamayı simüle edin​
  • Mevcut sahnenin Oyun sahnesine dönüştüğünü test et​
Unity projesinde Match 3 Starter/Tests'e gidin ve yeni bir C# Test Komut Dosyası oluşturun. MenuSuite olarak yeniden adlandırın.

2021-05-12_16-14-md.png

Dosyayı açın ve içeriği aşağıdaki kodla değiştirin:

C#:
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;

namespace Tests
{
    public class MenuSuite: InputTestFixture
    {
    }
}

Burada girdileri simüle etmek için InputTestFixture sınıfını kullanıyoruz. Daha önce hiç kullanmadıysanız, onu kapsayan Unity Test Framework eğitimimizi okumanızı öneririz.

Menü sahnesini yüklememiz gerekiyor ve bunu yapmak için bir Setup yöntemi oluşturacağız:

C#:
public override void Setup()
{
    base.Setup();
    SceneManager.LoadScene("Match 3 Starter/Scenes/Menu");
}

Bu, bir testi çalıştırmadan önce her seferinde sahneyi yükleyecektir.

Yeni bir UnityTest oluşturun:

C#:
[UnityTest]
public IEnumerator TestGameStart()
{
    yield return null;
}

Bir sonraki adım, Oynat düğmesini bulmaktır. Menü sahne hiyerarşisine bakarsanız MenuCanvas'ın altındaki düğmeyi bulabilirsiniz. PlayButton adlı tek düğme olduğu için Find yöntemini kullanabiliriz:

C#:
[UnityTest]
public IEnumerator TestGameStart()
{
    GameObject playButton = GameObject.Find("MenuCanvas/PlayButton");
    yield return null;
}

Artık play butonumuz olduğuna göre üzerine tıklamamız gerekiyor. Bunu başarmak için sanal bir fareye ihtiyacımız var, onu düğmenin konumuna taşıyın ve bir tıklamayı simüle edin.

Sınıfımıza bir fare ekleyelim:

C#:
public class MenuSuite: InputTestFixture
{
    Mouse mouse;

    public override void Setup()
    {
        base.Setup();
        SceneManager.LoadScene("Match 3 Starter/Scenes/Menu");
        mouse = InputSystem.AddDevice<Mouse>();
    }

Belirli bir GameObject'e tıklayan ClickUI adında yeni bir yöntem oluşturacağız:

C#:
public void ClickUI(GameObject uiElement)
{
    Camera camera = GameObject.Find("Main Camera").GetComponent<Camera>();
    Vector3 screenPos = camera.WorldToScreenPoint(uiElement.transform.position);
    Set(mouse.position, screenPos);
    Click(mouse.leftButton);
}

Bu yöntem kamerayı sahnede bulur ve GameObject'in konumunu ekran koordinatlarına aktarır. Daha sonra fareyi bu koordinatlara ayarlıyoruz ve bir tıklama olayını simüle ediyoruz.

Önemli: kamera ve tuval tam olarak aynı çözünürlükte oluşturulduğundan, koordinatların aktarılması burada işe yarar. Oyununuz farklı ayarlara sahipse, ekrandaki nesnenin doğru koordinatlarını elde etmek için daha fazla hesaplama gerekir.

Artık bu metoda sahip olduğumuza göre, onu kodumuzda kullanabiliriz:

C#:
[UnityTest]
public IEnumerator TestGameStart()
{
    GameObject playButton = GameObject.Find("MenuCanvas/PlayButton");

    ClickUI(playButton);
    yield return new WaitForSeconds(2f);
}

Bunu çalıştırırsanız, Menü'nün yüklendiğini, Oynat düğmesine tıklandığını ve ardından sahnenin oyuna geçtiğini göreceksiniz. Ancak, herhangi bir iddia kullanmadık, o yüzden biraz ekleyelim:

C#:
[UnityTest]
public IEnumerator TestGameStart()
{
    GameObject playButton = GameObject.Find("MenuCanvas/PlayButton");
    string sceneName = SceneManager.GetActiveScene().name;
    Assert.That(sceneName, Is.EqualTo("Menu"));

    ClickUI(playButton);
    yield return new WaitForSeconds(2f);

    sceneName = SceneManager.GetActiveScene().name;
    Assert.That(sceneName, Is.EqualTo("Game"));
}

Şimdi orijinal sahnenin menü olduğundan emin olduk ve ardından oyuna geçtik.

Tebrikler! Bir UI test otomasyonunu başarıyla yazdınız.

Hareket sayacı

İkinci otomatik testimiz için, her şeker takasında hareket sayısının azaldığını kontrol edeceğiz. İşte eylem planımız:
  • Oyun sahnesini yükleyin​
  • Hareket sayacını bulun​
  • 2 şekeri değiş tokuş et​
  • Hamle sayacının düştüğünü onayla​
Unity'de Match 3 Starter/Tests'e gidin ve yeni bir test komut dosyası oluşturun. GameSuite olarak yeniden adlandırın. Dosyayı açın ve içeriği aşağıdaki kodla değiştirin:

Kod:
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

namespace Tests
{
    public class GameSuite: InputTestFixture
    {
        Mouse mouse;

        public void ClickUI(GameObject uiElement)
        {
            Camera camera = GameObject.Find("Main Camera").GetComponent<Camera>();
            Vector3 screenPos = camera.WorldToScreenPoint(uiElement.transform.position);
            Set(mouse.position, screenPos);
            Click(mouse.leftButton);
        }

        public override void Setup()
        {
            base.Setup();
            SceneManager.LoadScene("Match 3 Starter/Scenes/Game");
            mouse = InputSystem.AddDevice<Mouse>();
        }
    }
}

Tekrar kullanacağımız için ClickUI yöntemini kopyaladığımızı unutmayın. Kodu çoğaltmak yerine, tüm yardımcı yöntemler ile bir ara sınıf oluşturup ondan miras almak daha iyi olacaktır. Ama öğretici için basit tutalım.

TestMoveCounterDecrease adında yeni bir otomatik test oluşturalım. Hareket sayısı özelliği GUIManager.instance.MoveCounter tarafından tutulur. Sonucun varsayılan değerden bağımsız olarak tahmin edilebilir olduğundan emin olmak için değerini ayarlayalım. Ardından, bu değişikliğin kullanıcı arayüzüne yansıtılıp yansıtılmadığını kontrol edebiliriz:

C#:
[UnityTest]
public IEnumerator TestMoveCounterDecrease()
{
    GUIManager.instance.MoveCounter = 10;
    GameObject moveCounterTxt = GameObject.Find("MoveCounterImage/MoveCounterTxt");
    string movesLeft = moveCounterTxt.GetComponent<Text>().text;
    Assert.That(movesLeft, Is.EqualTo("10"));
    yield return null;
}

Artık kalan hamle sayısının 10 olduğundan eminiz, şekerleri değiş tokuş edebiliriz ve düşüş olup olmadığına bakabiliriz. Bunu yapmak için sahneden ilgili karoları (şekerleri) almamız gerekiyor. Bunların keyfi bir sayısı vardır ve konumları da ona göre değişmektedir. Neyse ki oyun bize BoardManager.instance.tiles özelliği aracılığıyla ızgaraya erişim sağlıyor. ClickUI'ye istediklerimizi vermemiz yeterli:

C#:
[UnityTest]
public IEnumerator TestMoveCounterDecrease()
{
    GUIManager.instance.MoveCounter = 10;
    GameObject moveCounterTxt = GameObject.Find("MoveCounterImage/MoveCounterTxt");
    string movesLeft = moveCounterTxt.GetComponent<Text>().text;
    Assert.That(movesLeft, Is.EqualTo("10"));

    ClickUI(BoardManager.instance.tiles[0, 0]);
    yield return new WaitForSeconds(1f);
    ClickUI(BoardManager.instance.tiles[1, 0]);
    yield return new WaitForSeconds(2f);

    movesLeft = moveCounterTxt.GetComponent<Text>().text;
    Assert.That(movesLeft, Is.EqualTo("9"));
}

Testi çalıştırın ve sol üstteki şekerleri nasıl değiştirdiğini gözlemleyin, ardından başarılı bir şekilde geçin.

Menü üzerinden oyun

Hiç hamle kalmadığında oyun biter. Skorla birlikte görünen bir bindirme ve tekrar oynama veya menüye geri dönme yeteneği var.

Tekrar butonunu kontrol etmek için önceki testler üzerine inşa edeceğiz:
  • Oyun sahnesini yükleyin​
  • Şekerleri birkaç kez değiştirin​
  • Oyun bittiğinde play butonuna tıklayın.​
  • Yer paylaşımının gittiğini ve kartın değiştiğini onayla​
C#:
[UnityTest]
public IEnumerator TestGameOverPlayAgain()
{
    GUIManager.instance.MoveCounter = 3;
    BoardManager oldBoard = BoardManager.instance;

    for (int i = 0; i < 3; i++) {
        ClickUI(BoardManager.instance.tiles[0, 0]);
        yield return new WaitForSeconds(1f);
        ClickUI(BoardManager.instance.tiles[1, 0]);
        yield return new WaitForSeconds(1f);
    }

    GameObject playButton = GameObject.Find("GameOverPanel/PlayButton");
    ClickUI(playButton);

    yield return new WaitForSeconds(2f);

    string sceneName = SceneManager.GetActiveScene().name;
    Assert.That(sceneName, Is.EqualTo("Game"));
    Assert.That(GUIManager.instance.gameOverPanel.activeSelf, Is.EqualTo(false));
    Assert.That(BoardManager.instance, Is.Not.EqualTo(oldBoard));
}

Şimdi menü düğmesinin de çalışıp çalışmadığını kontrol edelim. Kod neredeyse aynı:

C#:
[UnityTest]
public IEnumerator TestGameOverBackToMenu()
{
    GUIManager.instance.MoveCounter = 3;
    BoardManager oldBoard = BoardManager.instance;

    for (int i = 0; i < 3; i++) {
        ClickUI(BoardManager.instance.tiles[0, 0]);
        yield return new WaitForSeconds(1f);
        ClickUI(BoardManager.instance.tiles[1, 0]);
        yield return new WaitForSeconds(1f);
    }

    GameObject playButton = GameObject.Find("GameOverPanel/MenuButton");
    ClickUI(playButton);

    yield return new WaitForSeconds(2f);

    string sceneName = SceneManager.GetActiveScene().name;
    Assert.That(sceneName, Is.EqualTo("Menu"));
}

Basit bir eşleşmeyi test etme

Şimdiye kadar yalnızca farklı menülerin, metin etiketlerinin ve sahne geçişlerinin entegrasyonunu kontrol ettik. Daha ileri gitmek için, şekerleri bir araya getiren temel oyunu test edebiliriz. İşte eylem planımız:
  • Oyun sahnesini yükleyin​
  • BoardManager'ı test için seçilen belirli bir şeker ızgarasını oluşturmaya zorlayın​
  • İlgili şekerleri değiştirin​
  • Skorun yükseldiğini onaylayın​
Buradaki önemli kısım, test için tasarlanmış belirli bir şeker ızgarası sağlamaktır. Bir kez daha, Unity projesi bunu kabul edecek kadar iyi inşa edildi.

Yeni bir otomatik test oluşturalım ve bir harf ızgarası oluşturalım. Her harf bir renge karşılık gelir: Mavi için B, kırmızı için R, vb:

C#:
[UnityTest]
public IEnumerator TestScoreSimpleMatch()
{
    string[,] grid = new string[3,3]{
        {"Y", "B", "M"},
        {"Y", "R", "G"},
        {"P", "Y", "M"}
    }; // Note that the grid's x and y dimensions are inverted here.
    BoardManager.instance.InitializeBoard(grid);

    yield return new WaitForSeconds(2f);
}

Olduğu gibi çalıştırırsanız, verilen renklere karşılık gelen yeni bir 3x3 şekerleme tahtası göreceksiniz. Şimdi görevimiz alttaki sarı şekeri seçip solundaki mor şekerle değiştirmek:

C#:
[UnityTest]
public IEnumerator TestScoreSimpleMatch()
{
    string[,] grid = new string[3,3]{
        {"Y", "B", "M"},
        {"Y", "R", "G"},
        {"P", "Y", "M"}
    }; // Note that the grid's x and y dimensions are inverted here.
    BoardManager.instance.InitializeBoard(grid);

    yield return new WaitForSeconds(1f);
    ClickUI(BoardManager.instance.tiles[1, 2]);
    yield return new WaitForSeconds(1f);
    ClickUI(BoardManager.instance.tiles[0, 2]);

    yield return new WaitForSeconds(2f);
}

Son olarak, puanın arttığından emin olmak için ilgili iddiaları ekliyoruz:

C#:
[UnityTest]
public IEnumerator TestScoreSimpleMatch()
{
    GameObject scoreTxt = GameObject.Find("ScorePanel/ScoreTxt");
    string[,] grid = new string[3,3]{
        {"Y", "B", "M"},
        {"Y", "R", "G"},
        {"P", "Y", "M"}
    }; // Note that the grid's x and y dimensions are inverted here.
    BoardManager.instance.InitializeBoard(grid);

    string score = scoreTxt.GetComponent<Text>().text;
    Assert.That(score, Is.EqualTo("0"));

    yield return new WaitForSeconds(1f);
    ClickUI(BoardManager.instance.tiles[1, 2]);
    yield return new WaitForSeconds(1f);
    ClickUI(BoardManager.instance.tiles[0, 2]);

    yield return new WaitForSeconds(2f);

    score = scoreTxt.GetComponent<Text>().text;
    Assert.That(score, Is.EqualTo("150"));
}

Bonus: daha verimli beklemeler

Eğitim sırasında sabit kodlanmış değere sahip beklemeler kullandığımızı fark etmiş olabilirsiniz. Bunun gibi basit testler için belirli bir saniye kadar beklemek yeterli olabilir. Ancak daha verimli beklemeler, bir koşulun karşılanıp karşılanmadığını kontrol etmemizi sağlayarak testi daha esnek ve sağlam hale getirir.

Bu eğitimde Unity UI nesnelerini, sahne geçişlerini ve giriş simülasyonu kullanımını otomatik olarak nasıl test edeceğimizi öğreneceğiz. UI test otomasyonu ile daha ileri gitmek istiyorsanız, işte size bazı tavsiyeler:
  • Oyun nesnelerinizi test edilmeleri için tasarlayın. BoardManager'da gördüğümüz gibi, sınıf, test kolaylığı için ilgili genel özelliklere ve yöntemlere sahiptir. Karmaşık bir nesneniz varsa, testlerin daha kolay yazılabilmesi için bazı parçalarını ortaya çıkarmak iyidir.​
  • Aradığınız nesnelerin adına dikkat edin. Hiyerarşinin farklı bölümlerinde iki nesne aynı ada sahip olabilir. Daraltmak için ebeveynlerini ve hatta etiketlerini (FindWithTag yöntemi aracılığıyla) kullanın.​
  • Kendinizi tekrar etmemek ve testi daha özlü hale getirmek için kendi faydalı sınıflarınızı yazmaktan çekinmeyin.​
 
Son düzenleme:
Paylaşım için teşekkürler.
 
Paylaşım için teşekkürler :)
 

Şu an konuyu görüntüleyenler (Toplam : 1, Üye: 0, Misafir: 1)

Geri
Üst