How to create simple ECS TEST

Wondering if someone could help me with this. So im trying to write simple test to validate code as I go but im not sure how to go about this. how do I run jobs from my system ?

For example …

using NUnit.Framework;

namespace Universe.Planet.Tests
{
    [TestFixture]
    [Category("ECS Test")]
    public class PlanetRuntimeTest
    {
        [Test]
        public void CheckEntityCount()
        {
            // Create test world
           
           // Initialize my system I want to test from my game

           // Run job in this system

           // Assert entity count

          // I assume i need some sort of cleanup
        }
    }
}
1 Like

You can create test world first, then add a single system then update world, then get entity manager from world to query the result. (EcsTesting/SystemTestBase.cs at master · 5argon/EcsTesting · GitHub)

You can also call update just that system instead of updating a world with 1 system. But there are various points I don’t like. For example if your system send some commands to ECBS then you will not see playback result unless you also add that to your test world by hand and also update it afterwards. If you use the world with 1 system approach and add ECBS systems to the world so updating the world also playback commands, they may not be the right place because the built in ECBS was fix-sorted by a special routine in the primary 3 component system groups code. Also if your system used changed filter as an update criteria / required, then you may need to manually cause dirty state so that the manual .Update call would work. (The .Update call on system still obey update conditions, it can’t force an update)

Or you can also test by viewing data combination as a unit instead of system. This way you can get the world to looks like at runtime. Create test world, add all systems like runtime, add data that you expect the system you want to test to transform them, then update world. (EcsTesting/WorldTestBase.cs at master · 5argon/EcsTesting · GitHub) Personally I only do it this way now since I could hack Time, I could see results from all ECB systems, I could test changed filter, etc. Some reviews of all approaches : https://gametorrahod.com/ecs-testing-review/

Unit testing is by far the bigger bang for buck, and at a minimum you shouldn’t be integration testing things you don’t also have unit tests for.

If you want to test what a job produces, then unit test what happens in a single Execute. IF for some reason that has side affects then you might need to test a sequence of Executes.

Be careful not to unit test ECS itself, that’s an easy mistake to make when starting to test. Sort of a cardinal rule in testing, don’t test other shit especially not third party stuff. Like say you want to test entities created in a job via ECB. You shouldn’t be testing ECB. You should be testing does your code call the ECB api’s with the right data the right number of times for the given input. To do that you can mock ECB, you could mock the job itself, etc…

There is nothing wrong with throwing in some integration level tests if you really need them, maybe ECS actually has a bug for example or you are using the api wrong and need to figure out what you are doing wrong. But that’s different then as a standard practice using integration testing while also not unit testing, which just flat out doesn’t work well. So if you want to start testing stuff as a practice then start with unit testing.

The most important thing here is keep your testing scope as limited as possible. There is nothing wrong with integration testing per say. It doesn’t really matter what label you use really. The larger lesson learned is the more isolated your tests, the more effective and less brittle they are.

1 Like

When I need to test system functionality I usually just copy Unity’s testing class and test the same way they do. You derive your testing class from ECSTestsFixture and go from there. It lets you write nice and simple test, schedule jobs, query, all that. You can see how Unity does it with their systems in Packages/Entities/Unity.Entities.Tests

3 Likes

Im trying to validate some entity data that is created at runtime but it doesn’t make sense to me how I can access the data from a system in my tests. To make thing harder I have one system creating the entities and another setting data c. Im also brand new to NUnit so really have no idea what im doing, spend a day reading docs but it falls apart for me when it comes to ECS. I also trolled your and 5argons gits to see if i can read over an example that would do what I need , any help or doc would be appreciated

Should look something like this:

using NUnit.Framework;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[TestFixture]
public class ECSTest : ECSTestsFixture
{
    [Test]
    public void SomeTest()
    {
        var systema = World.GetOrCreateSystem<SystemA>();
        var systemb = World.GetOrCreateSystem<SystemB>();

        var em = EmptySystem.EntityManager;
        var entity = em.CreateEntity(typeof(Whatever));

        World.Update();

        Assert.AreEqual(5, em.GetComponent<Whatever>(entity).value );
    }
}

If you need to query for a component do you can do EmptySystem.GetEntityQuery();

ahh ok, thank you, this look pretty straightforward, one questions. Does Update just do one “tick”

Yeah, it will basically run “OnUpdate” for every system in the world. You can also force all running jobs to complete with entityManager.CompleteAllJobs(), which is useful if you need to read your input data after a system processes it.

And just a bit of advice since I’ve been there, I remember the first time I started reading about testing, all the crazy terminology and sheer volume of material on the subject made my head spin. Mocking, integration tests, In my personal experience you can ignore 99% of it.

Almost every test I write follows the exact same pattern - declare inputs, declare what you’re testing, run the test, assert. Keep it simple and readable, don’t get bogged down trying to get fancy and writing a bunch of “utility” code for testing. I’ve rarely had to write a test much longer than what I posted above - if you find yourself doing so then the thing you’re testing might be trying to do too much.

That works well for me at least, your mileage may vary of course.

3 Likes

I ended up trying to do something like this but out of the box it didn’t update my system. It looks like World.Update() will only update the InitializationSystemGroup, SimulationSystemGroup, and PresentationSystemGroup and those groups don’t seem to be automatically created for me. I had to manually create one of those systems and add my own system to it’s update list. Something like:

[Test]
public void SomeTest()
{

    var group = World.GetOrCreateSystem<SimulationSystemGroup>();
    var systema = World.GetOrCreateSystem<SystemA>();
    var systemb = World.GetOrCreateSystem<SystemB>();

    group.AddSystemToUpdateList(systema);
    group.AddSystemToUpdateList(systemb);

    var em = EmptySystem.EntityManager;
    var entity = em.CreateEntity(typeof(Whatever));

    World.Update();

    Assert.AreEqual(5, em.GetComponent<Whatever>(entity).value );
}
1 Like

You’re right, that’s what I get for writing psuedo-code. It looks like in the Unity tests they explicitly update their systems too:
5440338--554691--devenv_CxAJkmL0uC.png

You can also use DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(w, systems) then your manually created world w will get ensured having the top 3 groups plus added your systems you want to test sorted to the correct group.

2 Likes

Still struggling with this, what do if i need to run a GameObjectConversionSystem first and query that data to validate it

Currently all my data is created through subScenes and GameObjectConversionSystem. Is there a way to start my whole world before a test.

Then can you use GameObjectConversionUtility and use ConvertScene? You can specify the test world on the conversion settings there as a destination world.

Do I need anything special for the subScene arg

GameObjectConversionSettings setting = new GameObjectConversionSettings(){DestinationWorld = m_PreviousWorld};
Scene scn = new Scene(){ name = "Assets/Universe/Planet.Scenes/UniverseMain/PlanetsSubScenes.unity"}; // ??
GameObjectConversionUtility.ConvertScene(scn, setting);

Try use SceneManager/EditorSceneManager to get a scene instead.

Thanks that fixed that problem. So should this give me a replica world ? , I can see my testWorld but it looking pretty sad

5456583--557226--Screen Shot 2020-02-07 at 10.23.06 PM.png

namespace Tests
{
    public class EditorTest : ECSTestsFixture
    {
        
        private PlanetConversionSystem TestSystem;

        [SetUp]
        public void SetUpTest()
        {
            Setup();
        }

        [TearDown]
        public void TearDownTest()
        {
            TearDown();
        }
        
        [Test]
        public void EditorTestSimplePasses()
        {
            var systems = new List<Type>();
            
            systems.AddRange(new []
            {
                typeof(PlanetConversionSystem),
                typeof(PlanetCreateTilesSystem),
                typeof(PlanetSetTileDataSystem),
                typeof(PlanetCreateMeshSystem)
            });
            
            DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(m_PreviousWorld, systems);
            
            GameObjectConversionUtility.ConvertScene(
                SceneManager.GetSceneByPath("Assets/Universe/Planet.Scenes/UniverseMain.unity"),
                new GameObjectConversionSettings(){DestinationWorld = m_PreviousWorld});
            
            Debug.Log(m_PreviousWorld.ToString());
            //GameObjectConversionSettings setting = new GameObjectConversionSettings(){DestinationWorld = m_PreviousWorld};

        }
    }
}

Shouldn’t I be seeing my added systems in the entity debugger? Actually I’m not seeing any system in test world or I’m I missing something here

I’m having trouble accessing assembly references in Unity.Entities.Tests to inherit from ECSTestFixture, but Unity.Entities is working.

Both assemblies are set as references in the inspector of my edit mode test assembly, but i’m getting a type/namespace does not exist error when trying to ad using.Unity.Entities.Tests;

Is there something I’m missing?

iirc, there was an issue with referencing a test assembly and trying to derive from their ECSTestFixtures. What I did was a I pretty much copied the structure of their TestFixture and plugged it into my own test assembly.

I’m not sure if an assembly definition reference will work - I don’t have much experience with trying that.