Как протестировать метод, который использует Random (), без аргументов и возвращаемого значения, используя JUnit или Mockito - PullRequest
0 голосов
/ 02 ноября 2018

Я учусь на Java-разработчика и сейчас изучаю разработку через тестирование, а это значит, что я очень плохо знаком с JUnit и Mockito.

Я некоторое время боролся, и я застрял.

Я понятия не имею, как протестировать этот конкретный метод, который не имеет аргументов, возвращаемого значения и рандомизатора.

Старая логика:

public void getPlayerToStart(int randomNr) {
    if (randomNr == 1) {
        currentPlayer = p1;
        opponentPlayer = p2;
    } else {
        currentPlayer = p2;
        opponentPlayer = p1;
    }
}

Старый тест

@Test
void testSetCurrentPlayerSetToPlayer1() {
    gameEngine.getPlayerToStart(1);
    assertEquals(gameEngine.getP1(), gameEngine.getCurrentPlayer());
    assertEquals(gameEngine.getP2(), gameEngine.getOpponentPlayer());
}

@Test
void testSetCurrentPlayerSetToPlayer2() {
    gameEngine.getPlayerToStart(2);
    assertEquals(gameEngine.getP2(), gameEngine.getCurrentPlayer());
    assertEquals(gameEngine.getP1(), gameEngine.getOpponentPlayer());
}

Новая логика:

public void getPlayerToStart() {
    Random rand = new Random();
    int randomNr = rand.nextInt(2) + 1;
    if (randomNr == 1) {
        currentPlayer = p1;
        opponentPlayer = p2;
    } else {
        currentPlayer = p2;
        opponentPlayer = p1;
    }
}

Я не уверен, как можно протестировать getPlayerToStart () без аргумента randomNr .. Может кто-нибудь, пожалуйста, просто укажите мне правильное направление!

Заранее спасибо.

Ответы [ 4 ]

0 голосов
/ 12 ноября 2018

Я согласен с тем, кто говорит, что вы должны использовать внедрение зависимостей и иметь собственную абстракцию (таким образом, вы можете издеваться над соавтором). Но, создавая абстракцию, вы просто переносите ответственность (и проблему тестирования) в другое место.

Знаете ли вы о конструкторе Random, который принимает целочисленный аргумент "seed"? Используя одно и то же семя, вы всегда будете иметь одинаковую последовательность результатов.

См .: https://stackoverflow.com/a/12458415/5594926

0 голосов
/ 02 ноября 2018

Всегда имейте в виду, что мысль "оу, это сложно проверить" - это то, что TDD пытается кричать , что дизайн нуждается в пересмотре.

Я понятия не имею, как протестировать этот конкретный метод, который не имеет аргументов, возвращаемого значения и рандомизатора.

Случайные числа являются побочным эффектом, таким как ввод / вывод или время, и должны обрабатываться таким образом в вашем проекте.

То есть, если вы делаете TDD, одна из вещей, которые вы должны признать, это то, что источником случайности является вход для вашей системы; это часть императивной оболочки , которая предоставляется вашим тестовым набором при выполнении тестов и предоставляется вашим корнем композиции в процессе производства.

Тестируемый подход отделил бы «генерацию семени» от «вычисления состояния из семени»; Модульные тесты отлично подходят для последнего бита, потому что чистые функции действительно легко тестировать. Генерация случайных чисел - это уровень уровня греха , который сложно проверить, но с некоторым дизайном вы можете упростить код вокруг него до такой степени, что он «явно не имеет недостатков».

Вы также можете просмотреть Написание проверяемого кода , автор Миско Хевери, или Сказки о короле-фишере .

0 голосов
/ 03 ноября 2018

другим решением может быть строгое толкование шаблона единой ответственности : класс, обеспечивающий бизнес-логику , не может нести ответственность за создание или приобретение его зависимостей. Это приводит к концепции внедрения зависимости :

class CodeUnderTest {
    private final Random rand;
    public CodeUnderTest(@NotNull Random rand){
        this.rand = rand;
    }

    public void getPlayerToStart() {
        int randomNr = rand.nextInt(2) + 1;
        if (randomNr == 1) {
            currentPlayer = p1;
            opponentPlayer = p2;
        } else {
            currentPlayer = p2;
            opponentPlayer = p1;
        }
    }
}

Вам нужно улучшить свой тест до этого:

class CodeUnderTestTest{
   private final Random fakeRandom = new Random(1);
   private CodeUnderTest cut;
   @Before
   public void setup(){
       cut = new CodeUnderTest(fakeRandom);
   }

   // your test code relying on the repeatable order
   // of Random object initialized with a fix seed.
}

Вам также нужно изменить все места в вашем коде, где вы создаете экземпляр CodeUnderTest, чтобы добавить объект Random без начального числа. Сначала это выглядит как недостаток, но дает возможность иметь только один экземпляр Random во всем коде без реализации Java Singelton Pattern .

Вы можете получить еще больший контроль, если замените объект Random на mock . Самый простой способ сделать это - использовать mocking framework наподобие Mockito :

class CodeUnderTestTest{       
   @Rule
   public MockitoRule rule = MockitoJUnit.rule();
   @Mock
   private Random fakeRandom;

// you could use @InjectMocks here
// instead of the setup method 
   private CodeUnderTest cut;
// This will NOT raise compile errors
// for not declared or not provided 
// constructor arguments (which is bad in my view).

   @Before
   public void setup(){
       cut = new CodeUnderTest(fakeRandom);
   }

   @Test
    void testSetCurrentPlayerSetToPlayer1() {
        doReturn(0).when(fakeRandom).nextInt(2);
        cut.getPlayerToStart(1);
        assertEquals(cut.getP1(), cut.getCurrentPlayer());
        assertEquals(cut.getP2(), cut.getOpponentPlayer());
    }
}
0 голосов
/ 02 ноября 2018

Переместите вызов на new Random() в свой собственный метод, например так.

Вы можете переписать свой getPlayerToStart метод, чтобы использовать другой, чтобы сохранить дублированный код (но вам это не нужно).

public void getPlayerToStart() {
    Random rand = makeRandom();
    int randomNumber = rand.nextInt(2) + 1
    getPlayerToStart(randomNumber);
}

public Random makeRandom() {
    return new Random();
}

Теперь вы можете использовать Mockito для

  • сделать макет Random объект;
  • сделайте шпионом своего класса, который вы хотите проверить;
  • заглушите метод makeRandom вашего шпиона, чтобы он возвращал ваш макет Random;
  • заглушить макет Random, чтобы он возвращал любое значение в каждом тесте.

После этого вы можете написать тест, в котором должен стартовать игрок 1, и еще один тест, в котором должен стартовать игрок 2.

...