MVC shop project for software architecture, in Unity.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

336 lines
15 KiB

using System;
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
namespace Tests
{
public class ShopUnitTests
{
private ShopView shopView; //This is the grid buy view we want to test
private SellViewGrid sellView;
private ShopView upgradeView;
//Setup the test scene
[OneTimeSetUp]
public void LoadShopScene()
{
// Load the Scene to do unit test. In the scope of this project, this is fine. In a more complicated project, a game scene could take
// a long time to load, in which case it's better to create test scenes to do unit tests
SceneManager.LoadScene(0);
}
//Setup the unit tests here
[UnitySetUp]
public IEnumerator SetupTests()
{
yield return
null; //yield return null skips one frame, this is to make sure that this happens after the scene is loaded
//The shop scene only contains one grid buy view, we use Resources.FindObjectsOfTypeAll to get the reference to it,
//Resources.FFindObjectsOfTypeAll is used instead of GameObject.Find because the later can't find disabled objects
var shops = Resources.FindObjectsOfTypeAll<ShopViewGrid>();
shopView = shops[2]; // For some reason this reverses it. We just want to hardcode it for this scene, no annoying for loops to compare
upgradeView = shops[1];
sellView = Resources.FindObjectsOfTypeAll<SellViewGrid>()[0];
// Turn on all views so they function correctly. We do not care for the looks during unit tests, obviously.
sellView.gameObject.SetActive(true);
upgradeView.gameObject.SetActive(true);
//Active the gridBuyView game object to initialize the class, if we don't do this 'void Start()' won't be called
//You should active all the game objects that are involved in the test before testing the functions from their components
shopView.gameObject.SetActive(true);
}
// Use meaningful name for your test cases, this case tests if the ShopGridBuyView component has initialized its ShopModel property
[UnityTest]
public IEnumerator ShopGridBuyViewInitializedShopModel()
{
yield return null; //yield return null skips one frame, waits for the Unity scene to load
//now test if a ShopModel is assigned to gridBuyView
Assert.IsNotNull(shopView.ShopModel, "No Model is assigned in ShopView");
}
//This case tests if the grid buy view displays the correct amount of Items
[UnityTest,Order(1)] // Run this one first to see if we populated everything correctly
public IEnumerator ShopGridBuyViewDisplaysCorrectAmountOfItems()
{
yield return null; //yield return null skips one frame, waits for the Unity scene to load
//Now that the scene is loaded and the gridBuyView game object was activated in SetupTests(), we can use GameObject.Find
//to find the game object we want to test
GameObject gridItemsPanel = GameObject.Find("GridItemsPanel");
yield return
new WaitForEndOfFrame(); //Since we are testing how many items are displayed, we should use WaitForEndOfFrame to wait until the end of the frame,
//so that the view finished updating and rendering everything
yield return null;
int itemCount = gridItemsPanel.transform.childCount;
Assert.AreEqual(shopView.ShopModel.inventory.GetItemCount(), itemCount,
"The generated item count is not equal to shopModel's itemCount");
}
// This case tests if the shop model gets populated with items
[UnityTest,Order(0)] // Run this one before the view tests to see if the factory works
public IEnumerator ShopFactoryPopulatesShop()
{
yield return null; //yield return null skips one frame, waits for the Unity scene to load
//Now that the scene is loaded and the gridBuyView game object was activated in SetupTests(), we can use GameObject.Find
//to find the game object we want to test
var count = 0;
Assert.DoesNotThrow(delegate
{
count = shopView.ShopModel.inventory.GetItemCount();
});
Assert.Greater(count,1); // Having just one item probably means something went wrong and crashed
}
//This case tests if the buyModel can throw an ArgumentOutOfRangeException when it's asked to select an item by a negative
//index. Incorrect indexes can be generated from bugs in views or controllers, throwing the correct type of exceptions is
//better than failing silently for debugging. Your unit tests should cover exception handlings
[UnityTest]
public IEnumerator ShopModelThrowsExceptionsWhenSelectingNegativeIndex()
{
//yield return null skips one frame, waits for the Unity scene to load and buyModel to be assigned
yield return null;
//Creates a delegate that call gridBuyView.ShopModel.SelectItemByIndex(-1), the test runner will run the function, and
//check if an ArgumentOutOfRangeException is thrown, the unit test would fail if no ArgumentOutOfRangeException
//was thrown
Assert.Throws<System.ArgumentOutOfRangeException>(delegate
{
shopView.ShopModel.SelectItemByIndex(-1);
});
}
//This case tests whether info panels and selection highlights work correctly when a specific item is selected, while disabling correctly when it is unselected
[UnityTest]
public IEnumerator UpdateViewItemInfoPanelAndSelection()
{
yield return null;
var selectedItem = shopView.transform.Find("GridItemsPanel");
var active = selectedItem.GetComponentInChildren<ViewItemInfoPanel>(); // In grid view, one item panel will be active. That's the selected item!
shopView.ShopModel.SelectItemByIndex(shopView.ShopModel.GetSelectedItemIndex() + 1);
var newActive = selectedItem.GetComponentInChildren<ViewItemInfoPanel>();
Assert.AreNotSame(active, newActive,"Previous item is still selected!");
}
// This test case tests that either no item is selected, or exactly one item is selected
[UnityTest]
public IEnumerator NoMoreThanOneItemSelectedInView()
{
yield return null;
var selectedItem = shopView.transform.Find("GridItemsPanel");
var active = selectedItem.GetComponentsInChildren<ViewItemInfoPanel>(); // In grid view, one item panel will be active. That's the selected item!
Assert.Greater(2,active.Length,"Too many items selected!"); // Depending on what's up, either we want none or just one selected now
shopView.ShopModel.SelectItemByIndex(shopView.ShopModel.GetSelectedItemIndex() + 1);
shopView.ShopModel.SelectItemByIndex(shopView.ShopModel.GetSelectedItemIndex() + 1);
active = selectedItem.GetComponentsInChildren<ViewItemInfoPanel>(); // In grid view, one item panel will be active. That's the selected item!
UnityEngine.Assertions.Assert.AreEqual(active.Length,1,"Too many items or no item selected!"); // Now we definitely need one to be selected!
}
// This test case tests if the currently selected item disappears from the view when the buy model confirms the selection
[UnityTest]
public IEnumerator ModelSelectionRemovesViewItem()
{
yield return null;
var selectedItem = shopView.transform.Find("GridItemsPanel");
shopView.ShopModel.SelectItemByIndex(shopView.ShopModel.GetSelectedItemIndex() + 1);
yield return null;
var active = selectedItem.GetComponentInChildren<ViewItemInfoPanel>(); // In grid view, one item panel will be active. That's the selected item!
shopView.ShopModel.ConfirmSelectedItem();
yield return null;
UnityEngine.Assertions.Assert.IsTrue(active == null,"Selected item view was not destroyed when bought!"); // Now we definitely need one to be selected!
}
// Tests if the player inventory doesn't allow to overdraw money. We test on shop inventory even though that one's money's technically irrelevant, but works the same on all
[UnityTest]
public IEnumerator InventoryDoesNotAllowExceedingTransactions()
{
yield return null;
// So if we pay too much, we expect an exception
Assert.Throws<InsufficientMoneyException>(delegate
{
shopView.ShopModel.inventory.ChangeBalance(-shopView.ShopModel.inventory.Money -
1); // Pay one money more than it's got!
});
}
// Tests if transaction changes actually change an inventory's balance
[UnityTest]
public IEnumerator InventoryCorrectlyHandlesBalanceChange()
{
yield return null;
var moneyBefore = shopView.ShopModel.inventory.Money;
shopView.ShopModel.inventory.ChangeBalance(-shopView.ShopModel.inventory.Money); // Spend all our money!
Assert.AreNotEqual(moneyBefore,shopView.ShopModel.inventory.Money);
Assert.Zero(shopView.ShopModel.inventory.Money);
shopView.ShopModel.inventory.ChangeBalance(moneyBefore + 1); // For good measure, re-add all money plus a one money bonus
Assert.AreEqual(moneyBefore + 1,shopView.ShopModel.inventory.Money);
}
// Tests if a model throws an error when trying to select an item directly that isn't part of it
[UnityTest]
public IEnumerator ModelSelectionErrorIfInvalid()
{
yield return null;
Assert.Throws<ArgumentException>(delegate
{
shopView.ShopModel.SelectItem(new ItemPotion("some name","invalidIcon",5,2,PotionType.Healing));
});
}
// Tests if a model selects an item correctly when it's valid
[UnityTest]
public IEnumerator ModelSelectionIndexCorrectIfValid()
{
yield return null;
var item = shopView.ShopModel.GetSelectedItem();
shopView.ShopModel.SelectItemByIndex(shopView.ShopModel.GetSelectedItemIndex() + 1);
Assert.AreNotSame(item,shopView.ShopModel.GetSelectedItem());
Assert.DoesNotThrow(delegate
{
shopView.ShopModel.SelectItem(item);
});
Assert.AreSame(item,shopView.ShopModel.GetSelectedItem());
}
// Tests if a buy model removes an item on confirmation, and then selects another one
[UnityTest]
public IEnumerator BuyModelConfirmationRemovesItemAndSelectsNew()
{
yield return null;
var item = shopView.ShopModel.GetSelectedItem();
shopView.ShopModel.ConfirmSelectedItem();
Assert.AreNotSame(item,shopView.ShopModel.GetSelectedItem());
Assert.Throws<ArgumentException>(delegate
{
shopView.ShopModel.SelectItem(item);
});
Assert.AreNotSame(item,shopView.ShopModel.GetSelectedItem());
}
// Tests if a buy model removes money from an inventory it buys from
[UnityTest]
public IEnumerator BuyingItemCostsMoney()
{
yield return null;
var money = sellView.ShopModel.inventory.Money; // In the scene setup, sellView happens to reference the player inventory the shop trades with
Assert.DoesNotThrow(delegate
{
shopView.ShopModel.ConfirmSelectedItem();
});
Assert.Less(sellView.ShopModel.inventory.Money,money);
}
// Tests if a sell model gains money from selling
[UnityTest]
public IEnumerator SellingItemGivesMoney()
{
yield return null;
var money = sellView.ShopModel.inventory.Money; // In the scene setup, sellView happens to reference the player inventory the shop trades with
Assert.DoesNotThrow(delegate
{
sellView.ShopModel.ConfirmSelectedItem();
});
Assert.Greater(sellView.ShopModel.inventory.Money,money);
}
// Tests if selling removes the item from the sell inventory
[UnityTest]
public IEnumerator SellingItemRemovesFromInventory()
{
yield return null;
var item = sellView.ShopModel.GetSelectedItem();
sellView.ShopModel.ConfirmSelectedItem();
Assert.AreNotSame(item,sellView.ShopModel.GetSelectedItem());
Assert.Throws<ArgumentException>(delegate
{
sellView.ShopModel.SelectItem(item);
});
Assert.AreNotSame(item,sellView.ShopModel.GetSelectedItem());
}
// Tests if upgrading an item does not remove it
[UnityTest]
public IEnumerator UpgradingKeepsInInventory()
{
yield return null;
var item = upgradeView.ShopModel.GetSelectedItem();
upgradeView.ShopModel.ConfirmSelectedItem();
Assert.AreSame(item,upgradeView.ShopModel.GetSelectedItem());
Assert.DoesNotThrow(delegate
{
upgradeView.ShopModel.SelectItem(item);
});
Assert.AreSame(item,upgradeView.ShopModel.GetSelectedItem());
}
// Tests if upgrading costs money
[UnityTest]
public IEnumerator UpgradingCostsMoney()
{
yield return null;
var money = upgradeView.ShopModel.inventory.Money; // In the scene setup, sellView happens to reference the player inventory the shop trades with
Assert.DoesNotThrow(delegate
{
upgradeView.ShopModel.ConfirmSelectedItem();
});
Assert.Less(upgradeView.ShopModel.inventory.Money,money);
}
// Tests if upgrading improves an item's stats
[UnityTest]
public IEnumerator UpgradingImprovesStats()
{
yield return null;
var item = upgradeView.ShopModel.GetSelectedItem(); // In the scene setup, sellView happens to reference the player inventory the shop trades with
var stats = item.GetStats(); // Stats right now are turned into a generic string type. If we get a different one post-upgrade, it should've worked
Assert.DoesNotThrow(delegate
{
upgradeView.ShopModel.ConfirmSelectedItem();
});
Assert.AreNotEqual(stats,upgradeView.ShopModel.GetSelectedItem().GetStats());
}
}
}