203 lines
8.0 KiB
Java
203 lines
8.0 KiB
Java
package fr.enssat.BoulderDash.models;
|
||
|
||
import fr.enssat.BoulderDash.helpers.AudioLoadHelper;
|
||
import fr.enssat.BoulderDash.helpers.LevelLoadHelper;
|
||
import org.junit.jupiter.api.BeforeEach;
|
||
import org.junit.jupiter.api.Test;
|
||
|
||
import java.util.Arrays;
|
||
|
||
import static org.mockito.Mockito.*;
|
||
import static org.junit.jupiter.api.Assertions.*;
|
||
|
||
public class LevelModelTest {
|
||
private LevelModel levelModel;
|
||
DisplayableElementModel[][] groundLevelModel;
|
||
|
||
LevelLoadHelper levelLoadHelper;
|
||
AudioLoadHelper audioLoadHelper;
|
||
GameInformationModel gameInformationModel;
|
||
|
||
@BeforeEach
|
||
public void setUp() {
|
||
levelLoadHelper = mock(LevelLoadHelper.class);
|
||
audioLoadHelper = mock(AudioLoadHelper.class);
|
||
gameInformationModel = mock(GameInformationModel.class);
|
||
|
||
// boundaries and 2x2 field inside
|
||
when(levelLoadHelper.getHeightSizeValue()).thenReturn(4);
|
||
when(levelLoadHelper.getWidthSizeValue()).thenReturn(4);
|
||
when(levelLoadHelper.getRockfordPositionX()).thenReturn(1);
|
||
when(levelLoadHelper.getRockfordPositionY()).thenReturn(1);
|
||
when(levelLoadHelper.getRockfordInstance()).thenReturn(new RockfordModel());
|
||
when(levelLoadHelper.getGroundGrid()).thenReturn(
|
||
new DisplayableElementModel[4][4]
|
||
);
|
||
// We add one diamond below, thus return 1.
|
||
when(levelLoadHelper.getDiamondsToCatch()).thenReturn(1);
|
||
|
||
levelModel = new LevelModel(levelLoadHelper, audioLoadHelper, "game");
|
||
|
||
// Place diamond next to Rockford
|
||
levelModel.getGroundLevelModel()[1][2] = new DiamondModel();
|
||
|
||
groundLevelModel = levelModel.getGroundLevelModel();
|
||
}
|
||
|
||
/**
|
||
* During an ongoing game, the player character is moved to an exit. The following
|
||
* behavior is expected and should be verified:
|
||
* • The game ends.
|
||
* • The player character is located on the field where the exit was previously located.
|
||
* • No sound is played.
|
||
*/
|
||
@Test
|
||
void test1bi(){
|
||
// Place exit next to Rockford
|
||
levelModel.getGroundLevelModel()[2][2] = new DoorModel();
|
||
|
||
// Move Rockford from (1,1) to (2,2)
|
||
levelModel.setPositionOfRockford(2,2);
|
||
|
||
// Verify:
|
||
// Game ended
|
||
assertFalse(levelModel.isGameRunning());
|
||
|
||
// Rockford is located at exit/door
|
||
assertEquals(2, levelModel.getRockfordPositionX());
|
||
assertEquals(2,levelModel.getRockfordPositionY());
|
||
|
||
// The sounds are played in a different thread after an action occurred.
|
||
// This might happen after we verify the number of method calls.
|
||
// To avoid this, we sleep one second.
|
||
try {
|
||
Thread.sleep(1000);
|
||
} catch (InterruptedException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
// No sound is played
|
||
verify(audioLoadHelper, never()).playSound(anyString());
|
||
}
|
||
|
||
/**
|
||
* During an ongoing game, the player character collects the last diamond. The
|
||
* following behavior is expected and should be verified:
|
||
* • The score increases by 1.
|
||
* • The number of remaining diamonds decreases to 0.
|
||
* • Exactly one exit appears on the game field.
|
||
* • The player character is located on the field where the diamond was previously located.
|
||
* • The starting field of the player character becomes an empty field.
|
||
* • The sound for collecting a diamond is played.
|
||
*/
|
||
@Test
|
||
void test1bii(){
|
||
int oldScore = levelModel.getGameInformationModel().getScore();
|
||
int oldRemainingDiamonds = levelModel.getGameInformationModel().getRemainingsDiamonds();
|
||
|
||
// Verify:
|
||
// 1 Remaining diamond
|
||
assertEquals(1, oldRemainingDiamonds);
|
||
// No exit.
|
||
assertEquals(0, numOfExits());
|
||
|
||
// Move Rockford from (1,1) to (1,2)
|
||
levelModel.setPositionOfRockford(1,2);
|
||
|
||
// Verify:
|
||
// Score increased by 1
|
||
assertEquals(oldScore + 1, levelModel.getGameInformationModel().getScore());
|
||
// No remaining diamonds
|
||
assertEquals(0, levelModel.getGameInformationModel().getRemainingsDiamonds());
|
||
|
||
// Verify: One exit appeared.
|
||
// Math.random -> sometimes spawned at old or new position of Rockford,
|
||
// then this assertion fails
|
||
assertEquals(1, numOfExits());
|
||
|
||
// Rockford placed at previous diamond location
|
||
assertEquals(1, levelModel.getRockfordPositionX());
|
||
assertEquals(2,levelModel.getRockfordPositionY());
|
||
|
||
// The starting field of Rockford becomes an empty field
|
||
assertInstanceOf(EmptyModel.class, groundLevelModel[1][1]);
|
||
|
||
// The sounds are played in a different thread after an action occurred.
|
||
// This might happen after we verify the number of method calls (which would make this test fail).
|
||
// To avoid this, we sleep one second.
|
||
try {
|
||
Thread.sleep(1000);
|
||
} catch (InterruptedException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
// The sound for collecting a diamond is played.
|
||
verify(audioLoadHelper).playSound("coin");
|
||
}
|
||
|
||
int numOfExits(){
|
||
// We can't use
|
||
// verify(levelModel, times(1)).spawnExit();
|
||
// as spawnExit() is a private method.
|
||
|
||
// Iterate over the ground level and count DoorModel objects.
|
||
return (int) Arrays.stream(groundLevelModel)
|
||
.flatMap(Arrays::stream)
|
||
.filter(x -> x instanceof DoorModel)
|
||
.count();
|
||
}
|
||
|
||
/**
|
||
* During an ongoing game, the player character collects the last diamond again.
|
||
* However, this test case should minimize concrete state changes in the application and instead verify that the appropriate methods are called with the
|
||
* correct parameters to achieve the desired behavior. Explain and justify for
|
||
* which of the following points this is not possible. The following behavior is
|
||
* expected and should be verified:
|
||
* • The increase in score is triggered.
|
||
* • The decrease in the number of remaining diamonds is triggered.
|
||
* • The appearance of an exit on the game field is triggered.
|
||
* • The update of the player character’s position is triggered.
|
||
* • Replacing the player character’s starting field with an empty field is triggered.
|
||
* • Playing the sound for the entered field is triggered.
|
||
*/
|
||
@Test
|
||
void test1biii(){
|
||
GameInformationModel gameInformationModel = mock(GameInformationModel.class);
|
||
levelModel.setGameInformationModel(gameInformationModel);
|
||
|
||
levelModel = spy(levelModel);
|
||
|
||
// Move Rockford from (1,1) to (1,2)
|
||
levelModel.setPositionOfRockford(1,2);
|
||
|
||
// Verify:
|
||
// increase score
|
||
verify(gameInformationModel).incrementScore();
|
||
// decrease remaining diamonds
|
||
verify(gameInformationModel).decrementRemainingsDiamonds();
|
||
|
||
// exit appearance - IMPOSSIBLE
|
||
// 'spawnExit()' has private access
|
||
// We can only verify there are no remaining diamonds
|
||
verify(gameInformationModel).getRemainingsDiamonds();
|
||
|
||
// update character's position
|
||
verify(levelModel).updateRockfordPosition(anyInt(), anyInt());
|
||
|
||
// replace with empty field - IMPOSSIBLE
|
||
// We can't verify if the field at the position is being emptied,
|
||
// because this happens inside the method setPositionOfRockford using a constructor call:
|
||
// `this.groundGrid[oldX][oldY] = new EmptyModel();`
|
||
assertInstanceOf(EmptyModel.class, groundLevelModel[1][1]);
|
||
|
||
// The sounds are played in a different thread after an action occurred.
|
||
// This might happen after we verify the number of method calls (which would make this test fail).
|
||
// To avoid this, we sleep one second.
|
||
try {
|
||
Thread.sleep(1000);
|
||
} catch (InterruptedException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
// The sound for collecting a diamond is played.
|
||
verify(audioLoadHelper).playSound("coin");
|
||
}
|
||
}
|