Dungeon-CampusMinden / Dungeon

The "Dungeon" is a tool to gamify classroom content and integrate it into a 2D Rogue-Like role-playing game.
MIT License
17 stars 36 forks source link

[Game] Idee, um die Klasse starter.Game zu testen #659

Closed tgrothe closed 11 months ago

tgrothe commented 1 year ago

PR #656 hatte mich auf die Idee gebracht, wie die Game-Klasse getestet werden könnte. Hier wäre mein Vorschlag:

build.gradle:

test {
    jvmArgs += "--add-opens=java.logging/java.util.logging=ALL-UNNAMED"
    jvmArgs += "--add-opens=java.base/java.nio=ALL-UNNAMED"
    testLogging {
        events "passed", "skipped", "failed"
    }
}

game/test/starter/GameTest.java:

package starter;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Graphics;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Files;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.utils.GdxNativesLoader;
import ecs.entities.Entity;
import ecs.entities.Hero;
import graphic.DungeonCamera;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.logging.LogManager;
import java.util.logging.Logger;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

@RunWith(PowerMockRunner.class)
@PrepareForTest({Logger.class, LogManager.class, Gdx.class, Application.class, Graphics.class, GL20.class, SpriteBatch.class, ShaderProgram.class, DungeonCamera.class})
class GameTest {
    Game game;
    Entity hero;
    int someArbitraryValueGreater0forDelta = 7;

    // Because of use of PowerMockRunner we need an empty constructor here
    public GameTest() {
    }

    @Before
    public void setUp() {
        PowerMockito.mockStatic(Logger.class);

        GdxNativesLoader.load();
        Gdx.app = PowerMockito.mock(Application.class);
        Gdx.graphics = PowerMockito.mock(Graphics.class);
        Gdx.input = PowerMockito.mock(Input.class);
        Gdx.files = new Lwjgl3Files();
        Gdx.gl = PowerMockito.mock(GL20.class);
        Gdx.gl20 = PowerMockito.mock(GL20.class);
        PowerMockito.mockStatic(SpriteBatch.class);
        PowerMockito.when(SpriteBatch.createDefaultShader()).thenReturn(PowerMockito.mock(ShaderProgram.class));

        game = new Game();
        LibgdxSetup setup = new LibgdxSetup(game);
        setup.create();
        game.render(0);
        hero = Game.getHero().orElseThrow();
    }

    @Test
    public void heroTest() {
        assertNotNull(hero);
        assertTrue(hero instanceof Hero);
    }

    @Test
    public void addEntity() {
        Entity e1 = Mockito.mock(Entity.class);
        Game.addEntity(e1);
        assertTrue(Game.getEntitiesToAdd().contains(e1));
        assertEquals(1, Game.getEntitiesToAdd().size());
        Game.getEntities().clear();
        Game.getEntitiesToAdd().clear();
    }

    @Test
    public void removeEntity() {
        Entity e1 = Mockito.mock(Entity.class);
        Game.removeEntity(e1);
        assertTrue(Game.getEntitiesToRemove().contains(e1));
        assertEquals(1, Game.getEntitiesToRemove().size());
        Game.getEntities().clear();
        Game.getEntitiesToRemove().clear();
    }

    @Test
    public void setHero() {
        Entity hero = Mockito.mock(Entity.class);
        Game.setHero(hero);
        assertEquals(hero, Game.getHero().get());
        Entity hero2 = Mockito.mock(Entity.class);
        Game.setHero(hero2);
        assertEquals(hero2, Game.getHero().get());
    }
}

Damit würde eben alles, bis auf den Libgdx-Kontext, "aufgebaut" werden und die Logik könnte getestet werden.

hth

cagix commented 1 year ago

@tgrothe Wir wollen Powermock eigentlich loswerden und nicht noch tiefer integrieren - das Powermock-Projekt ist seit längerer Zeit ohne Pflege, wie es scheint.

tgrothe commented 1 year ago

@tgrothe Wir wollen Powermock eigentlich loswerden und nicht noch tiefer integrieren - das Powermock-Projekt ist seit längerer Zeit ohne Pflege, wie es scheint.

Ja gut, aber dann hätte ich im Moment keine alternative Idee.

tgrothe commented 1 year ago

@cagix Ich hab mich schlau gemacht, es ist tatsächlich mit Mockito 5 komplett möglich (static mocking):

dependencies {
    // LibGDX
    implementation "com.badlogicgames.gdx:gdx:$gdxVersion"
    implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    implementation "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
    implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
    implementation "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
    implementation "com.badlogicgames.gdx:gdx-ai:$aiVersion"

    // Mockito 5
    implementation 'org.mockito:mockito-core:5.3.1'

    // JUnit 4
    testImplementation "junit:junit:4.+"

    // https://mvnrepository.com/artifact/com.google.code.gson/gson
    implementation group: "com.google.code.gson", name: "gson", version: gsonVersion

    // ANTLR version 4 for DSL Grammar
    antlr "org.antlr:antlr4:4.11.1"
}
package starter;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Graphics;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Files;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.utils.GdxNativesLoader;
import ecs.entities.Entity;
import ecs.entities.Hero;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedStatic;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;

public class GameTest {
    Game game;
    Entity hero;
    MockedStatic<SpriteBatch> spriteBatchMockedStatic;

    @Before
    public void setUp() {
        GdxNativesLoader.load();
        Gdx.app = mock(Application.class);
        Gdx.graphics = mock(Graphics.class);
        Gdx.input = mock(Input.class);
        Gdx.files = new Lwjgl3Files();
        Gdx.gl = mock(GL20.class);
        Gdx.gl20 = mock(GL20.class);
        spriteBatchMockedStatic = mockStatic(SpriteBatch.class);
        spriteBatchMockedStatic.when(SpriteBatch::createDefaultShader).thenReturn(mock(ShaderProgram.class));

        game = new Game();
        LibgdxSetup setup = new LibgdxSetup(game);
        setup.create();
        game.render(0);
        hero = Game.getHero().orElseThrow();
    }

    @After
    public void tearDown() {
        spriteBatchMockedStatic.close();
    }

    @Test
    public void heroTest() {
        assertNotNull(hero);
        assertTrue(hero instanceof Hero);
    }

    @Test
    public void addEntity() {
        Entity e1 = mock(Entity.class);
        Game.addEntity(e1);
        assertTrue(Game.getEntitiesToAdd().contains(e1));
        assertEquals(1, Game.getEntitiesToAdd().size());
        Game.getEntities().clear();
        Game.getEntitiesToAdd().clear();
    }

    @Test
    public void removeEntity() {
        Entity e1 = mock(Entity.class);
        Game.removeEntity(e1);
        assertTrue(Game.getEntitiesToRemove().contains(e1));
        assertEquals(1, Game.getEntitiesToRemove().size());
        Game.getEntities().clear();
        Game.getEntitiesToRemove().clear();
    }

    @Test
    public void setHero() {
        Entity hero = mock(Entity.class);
        Game.setHero(hero);
        assertEquals(hero, Game.getHero().get());
        Entity hero2 = mock(Entity.class);
        Game.setHero(hero2);
        assertEquals(hero2, Game.getHero().get());
    }
}

Wenn ich darf, dann würde ich die alten Powermock-Tests gerne überarbeiten (~ 5 Klassen) und einen PR stellen.

Siehe auch: https://stackoverflow.com/questions/34917532/are-there-any-good-alternatives-to-powermock