[JAVA] Maintenez l'image pratique et l'image essentielle pour lutter contre le TDD

introduction

Cet article est écrit pour s'attaquer au TDD et pour partager avec d'autres une image essentielle du TDD.

Qu'est-ce que TDD

Technique de conception / analyse, pas technique de test

--Valeur

Qu'est-ce que la conception incrémentielle?

――Pour rendre quelque chose de précieux aussi efficacement que possible «Parce que plus vous comprenez l'objectif de développement, plus vous pouvez prendre de bonnes décisions. ――Je ne déciderai pas tant que je n'aurai pas pris la bonne décision sur ce qu'il faut faire ――Nous nous développons progressivement à partir des fonctions minimales requises et faisons évoluer progressivement la conception elle-même

Cas approprié: Agile ・ Je ne sais pas ce que je veux faire ・ L'équipe de développement ne comprend pas entièrement l'objectif de développement ・ L'objectif de développement est compliqué et personne ne connaît la bonne conception

couler

--Rouge: Ecrire un test qui échoue (ajout de fonctions / ajout de conditions / reproduction de bogues etc.)

En passant par le cycle ci-dessus, nous allons concevoir progressivement en fonction du rythme, de la confiance et de la confiance.

Points à faire

Échantillon d'image pratique

Sujet: calcul du score de bowling environnement d'utilisation:

Tout d'abord, la solution la plus simple et la plus simple

Tous les étuis porte-jarretelles

ligne de départ

Le premier test à ajouter est d'écrire un test "ne rien faire"

Red

Untitled 2.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  @Test
  public void test_all_Garter() {
    var game = new BowlingGame();
    assertThat(game).isNotNull();
  }
}

Green

Ajouter une classe Untitled 3.png

public class BowlingGame {

}

Ajoutez la plus petite méthode

Red

Faites-en un cas de test pour toutes les jarretières de lancer

Untitled 4.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  @Test
  public void test_all_Garter() {
    var game = new BowlingGame();
+   for(int count = 0; count < 20; count++) {
+     game.recordOneShot(0);
+   }
-   assertThat(game).isNotNull();
+   assertThat(game.getTotalScore()).isEqualTo(0);
  }
}

Green

Untitled 5.png

public class BowlingGame {

+ public void recordOneShot(int pins) {
+ }

+ public int score() {
+   return 0;
+ }
}

Cas où une seule broche est renversée

Red Untitled 7.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  // ry

+ @Test
+ public void test_all_one_pin() {
+   var game = new BowlingGame();
+   for (int count = 0; count < 20; count++) {
+     game.recordOneShot(1);
+   }
+   assertThat(game.score()).isEqualTo(20);
+ }
}

Green

Untitled 8.png

public class BowlingGame {

  private int totalScore = 0;

+ public void recordOneShot(int pins) {
+   totalScore += pins;
+ }

+ public int getTotalScore() {
+   return totalScore;
+ }
}

Refactoriser si ça sent mauvais

Déclenché par «l'odeur sinistre» du code

--Code en double

//Refactor de code de test (suppression de code en double: extraction de méthode)
import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

+ private BowlingGame game;
+
+ @BeforeEach
+ public void setup() {
+   game = new BowlingGame();
+ }

  @Test
  public void test_test_all_Garter() {
-   var game = new BowlingGame();
-   for(int count = 0; count < 20; count++) {
-     game.recordOneShot(0);
-   }
+   recordSamePinsManyShot(20, 0);
    assertThat(game.getTotalScore()).isEqualTo(0);
  }

  @Test
  public void test_all_one_pin() {
-   var game = new BowlingGame();
-   for (int count = 0; count < 20; count++) {
-     game.recordOneShot(1);
-   }
+   recordSamePinsManyShot(20, 1);
    assertThat(game.getTotalScore()).isEqualTo(20);
  }

+ private void recordSamePinsManyShot(int shotCount, int pins) {
+   for (int count = 0; count < shotCount; count++) {
+     game.recordOneShot(pins);
+   }
+ }
}

Un cas un peu compliqué: Spare (le nombre de broches pour le prochain tour sera ajouté)

Red

Untitled 9.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_spare() {
+   game.recordOneShot(4);
+   game.recordOneShot(6); //Réserve 10+5=15
+   game.recordOneShot(5);
+   recordSamePinsManyShot(17, 0);
+   assertThat(game.getTotalScore()).isEqualTo(20);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

Façon de penser lors des corrections

↓ Essayez de réparer

public class BowlingGame {

  private int totalScore = 0;
+ private boolean isSpare = false;

  public void recordOneShot(int pins) {
    totalScore += pins;
+   if (isSpare) {
+     totalScore += pins;
+     isSpare = false;
+   }
+   if (totalScore == 10) {
+     isSpare = true;
+   }
  }

  public int getTotalScore() {
    return totalScore;
  }
}

Untitled 10.png

--Fixé pour réussir si le test existant échoue --Cause → Parce que le drapeau était activé lorsque le score total était de 10.

Untitled 11.png

package bowling;

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
+ private int beforePins = 0;

  public void recordOneShot(int pins) {
    totalScore += pins;
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
-   if (totalScore == 10) {
+   if (pins + beforePins == 10) {
      isSpare = true;
    }
+   beforePins = pins;
  }

  public int getTotalScore() {
    return totalScore;
  }
}

Testons l'angle mort de l'implémentation actuelle

Red

Untitled 12.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry
 
+ @Test
+ public void test_not_spare_when_before_and_current_total_10() {
+   game.recordOneShot(2);
+   game.recordOneShot(6);
+   game.recordOneShot(4);
+   game.recordOneShot(7);
+   recordSamePinsManyShot(16, 0);
+   assertThat(game.getTotalScore()).isEqualTo(19);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

Green

Untitled 13.png

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;

  public void recordOneShot(int pins) {
    totalScore += pins;
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
-   if (pins + beforePins == 10) {
+   if (shotCount == 2 && pins + beforePins == 10) {
      isSpare = true;
    }

    beforePins = pins;
+   shotCount = shotCount == 1 ? 2 : 1;
  }

  public int getTotalScore() {
    return totalScore;
  }
}

5. Cas de test complexe: Strike / Continuous Strike

la grève

Red Untitled 14.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_strike() {
+   game.recordOneShot(10);
+   game.recordOneShot(3);
+   game.recordOneShot(4);
+   game.recordOneShot(2);
+   recordSamePinsManyShot(15, 0);
+   assertThat(game.getTotalScore()).isEqualTo(26);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

--Fixé --Cause: la grève ne peut pas être déterminée

Green Untitled 15.png

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;
+ private int strikeAddScoreCount = 0;

  public void recordOneShot(int pins) {
    totalScore += pins;
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
    if (shotCount == 2 && pins + beforePins == 10) {
      isSpare = true;
    }

+   if (strikeAddScoreCount > 0) {
+     totalScore += pins;
+     strikeAddScoreCount -= 1;
+   }
+   if (pins == 10) {
+     strikeAddScoreCount = 2;
+   }

    beforePins = pins;
    shotCount = shotCount == 1 ? 2 : 1;

  }

  public int getTotalScore() {
    return totalScore;
  }
}

Refactoring régulier

Extraction de méthode


package bowling;

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;
  private int strikeAddScoreCount = 0;

  public void recordOneShot(int pins) {
    totalScore += pins;
-   if (isSpare) {
-     totalScore += pins;
-     isSpare = false;
-   }
-   if (shotCount == 2 && pins + beforePins == 10) {
-     isSpare = true;
-   }
-
-   if (strikeAddScoreCount > 0) {
-     totalScore += pins;
-     strikeAddScoreCount -= 1;
-   }
-   if (pins == 10) {
-     strikeAddScoreCount = 2;
-   }

+   calcSpareAddScore(pins);
+   calcStrikeAddScore(pins);
    beforePins = pins;
    shotCount = shotCount == 1 ? 2 : 1;
  }

+ public int getTotalScore() {
+   return totalScore;
+ }
+
+ private void calcSpareAddScore(int pins) {
+   if (isSpare) {
+     totalScore += pins;
+     isSpare = false;
+   }
+   if (shotCount == 2 && pins + beforePins == 10) {
+     isSpare = true;
+   }
+ }
+
+ private void calcStrikeAddScore(int pins) {
+   if (strikeAddScoreCount > 0) {
+     totalScore += pins;
+     strikeAddScoreCount -= 1;
+   }
+   if (pins == 10) {
+     strikeAddScoreCount = 2;
+   }
+ }
}

2 grèves consécutives

Red Untitled 17.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_strike_double() {
+   game.recordOneShot(10);
+   game.recordOneShot(10);
+   game.recordOneShot(4);
+   game.recordOneShot(2);
+   recordSamePinsManyShot(14, 0);
+   assertThat(game.getTotalScore()).isEqualTo(46);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

Green Untitled 18.png


public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;
  private int strikeAddScoreCount = 0;
+ private int doubleAddScoreCount = 0;

  public void recordOneShot(int pins) {
    totalScore += pins;
    calcSpareAddScore(pins);
    calcStrikeAddScore(pins);
    beforePins = pins;
    shotCount = shotCount == 1 ? 2 : 1;
  }

  public int getTotalScore() {
    return totalScore;
  }

  private void calcSpareAddScore(int pins) {
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
    if (shotCount == 2 && pins + beforePins == 10) {
      isSpare = true;
    }
  }

  private void calcStrikeAddScore(int pins) {
    if (strikeAddScoreCount > 0) {
      totalScore += pins;
      strikeAddScoreCount -= 1;
    }
+   if (doubleAddScoreCount > 0) {
+     totalScore += pins;
+     doubleAddScoreCount -= 1;
+   }
    if (pins == 10) {
+     if (strikeAddScoreCount == 0) {
        strikeAddScoreCount = 2;
+     } else {
+       doubleAddScoreCount = 2;
+     }
    }
  }
}

Ajout de cas de test pour 3 frappes consécutives

Green Untitled 19.png


import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_strike_turkey() {
+   game.recordOneShot(10);
+   game.recordOneShot(10);
+   game.recordOneShot(10);
+   game.recordOneShot(4);
+   game.recordOneShot(2);
+   recordSamePinsManyShot(12, 0);
+   assertThat(game.getTotalScore()).isEqualTo(76);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

Cas complexe (suite): frappe et secours combinés

1 gâche et 1 de rechange

Red Untitled 20.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_strike_and_spare() {
+   game.recordOneShot(10);
+   game.recordOneShot(6);
+   game.recordOneShot(4);
+   game.recordOneShot(2);
+   recordSamePinsManyShot(15, 0);
+   assertThat(game.getTotalScore()).isEqualTo(34);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

--Cause: shotCount ne revient pas à 1 après l'attaque

Green Untitled 21.png

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;

  public void recordOneShot(int pins) {
    totalScore += pins;
    calcSpareAddScore(pins);
    calcStrikeAddScore(pins);
    beforePins = pins;
-   shotCount = shotCount == 1 ? 2 : 1;
+   shotCount = shotCount == 1 && strikeAddScoreCount < 2 ? 2 : 1;
  }

  public int getTotalScore() {
    return totalScore;
  }

  private void calcSpareAddScore(int pins) {
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
    if (shotCount == 2 && pins + beforePins == 10) {
      isSpare = true;
    }
  }

  private void calcStrikeAddScore(int pins) {
    if (strikeAddScoreCount > 0) {
      totalScore += pins;
      strikeAddScoreCount -= 1;
    }
    if (doubleAddScoreCount > 0) {
      totalScore += pins;
      doubleAddScoreCount -= 1;
    }
    if (pins == 10) {
      if (strikeAddScoreCount == 0) {
        strikeAddScoreCount = 2;
      } else {
        doubleAddScoreCount = 2;
      }
    }
  }
}

2 coups consécutifs + rechange

Red Untitled 22.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_strike_double_and_spare() {
+   game.recordOneShot(10);
+   game.recordOneShot(10);
+   game.recordOneShot(6);
+   game.recordOneShot(4);
+   game.recordOneShot(2);
+   recordSamePinsManyShot(13, 0);
+   assertThat(game.getTotalScore()).isEqualTo(26+20+12+2);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

--Cause: shotCount ne revient pas à 1 même après 2 frappes consécutives

Green Untitled 23.png

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;

  public void recordOneShot(int pins) {
    totalScore += pins;
    calcSpareAddScore(pins);
    calcStrikeAddScore(pins);
    beforePins = pins;
-   shotCount = shotCount == 1 && strikeAddScoreCount < 2 ? 2 : 1;
+   shotCount = shotCount == 1 && strikeAddScoreCount < 2 && doubleAddScoreCount < 2 ? 2 : 1;
  }

  public int getTotalScore() {
    return totalScore;
  }

  private void calcSpareAddScore(int pins) {
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
    if (shotCount == 2 && pins + beforePins == 10) {
      isSpare = true;
    }
  }

  private void calcStrikeAddScore(int pins) {
    if (strikeAddScoreCount > 0) {
      totalScore += pins;
      strikeAddScoreCount -= 1;
    }
    if (doubleAddScoreCount > 0) {
      totalScore += pins;
      doubleAddScoreCount -= 1;
    }
    if (pins == 10) {
      if (strikeAddScoreCount == 0) {
        strikeAddScoreCount = 2;
      } else {
        doubleAddScoreCount = 2;
      }
    }
  }
}

Refactor (extraction de méthode)

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;

  public void recordOneShot(int pins) {
    totalScore += pins;
    calcSpareAddScore(pins);
    calcStrikeAddScore(pins);
    beforePins = pins;
    shotCount = shotCount == 1 && strikeAddScoreCount < 2 && doubleAddScoreCount < 2 ? 2 : 1;
  }

  public int getTotalScore() {
    return totalScore;
  }

  private void calcSpareAddScore(int pins) {
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
    checkSpare(pins);
  }

  private void checkSpare(int pins) {
    if (shotCount == 2 && pins + beforePins == 10) {
      isSpare = true;
    }
  }

  private void calcStrikeAddScore(int pins) {
-   if (strikeAddScoreCount > 0) {
-     totalScore += pins;
-     strikeAddScoreCount -= 1;
-   }
-   if (doubleAddScoreCount > 0) {
-     totalScore += pins;
-     doubleAddScoreCount -= 1;
-   }
-   if (pins == 10) {
-     if (strikeAddScoreCount == 0) {
-       strikeAddScoreCount = 2;
-     } else {
-       doubleAddScoreCount = 2;
-     }
-   }
+   addStrikeScore(pins);
+   addDoubleScore(pins);
+   if (isStrike(pins)) {
+     recognizeStrikeAddCount();
+   }
  }

+ private void addStrikeScore(int pins) {
+  if (strikeAddScoreCount > 0) {
+     totalScore += pins;
+     strikeAddScoreCount -= 1;
+   }
+ }
+
+ private void addDoubleScore(int pins) {
+   if (doubleAddScoreCount > 0) {
+     totalScore += pins;
+     doubleAddScoreCount -= 1;
+   }
+ }
+
+ private boolean isStrike(int pins) {
+   return pins == 10;
+ }
+
+ private void recognizeStrikeAddCount() {
+   if (strikeAddScoreCount == 0) {
+     strikeAddScoreCount = 2;
+   } else {
+     doubleAddScoreCount = 2;
+   }
+ }
}

Obtenez des points pour chaque image

Red Untitled 24.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_one_frame_score_when_all_garter() {
+   recordSamePinsManyShot(20, 0);
+   assertThat(game.getFrameScore(1)).isEqualTo(0);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

Green Untitled 25.png

public BowlingGame {

  // ry

+ public int getFrameScore(int frameNum) {
+   return 0;
+ }
}

Passez en revue la conception statique si vous sentez une impasse

――Lorsque vous essayez d'ajouter un cas de test qui doit être calculé ... Sera-t-il répété jusqu'à présent?

Classe de trame introduite

public class Frame {

  public void recordOneShot(int pins) {
  }

  public int getScore(int frameNum) {
    return 0;
  }
}

Lorsqu'une seule broche de tous les lancers est vaincue

Red

Untitled 27.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  @Test
  public void test_one_frame_score_when_all_one_pin() {
    frame.recordOneShot(1);
    assertThat(frame.getScore(1)).isEqualTo(1);
  }
}

Green Untitled 28.png

public class Frame {

  private int score = 0;
  
  public void recordOneShot(int pins) {
+   score += pins;
  }

  public int getScore(int frameNum) {
-   return 0;
+   return score;
  }
}

Cadre intégré au jeu de bowling

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;
+ private Frame frame = new Frame();

  // ry

+ public int getFrameScore(int frameNum) {
+   return frame.getScore(frameNum);
+ }

  // ry

}
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_one_frame_score_when_all_garter() {
+   recordSamePinsManyShot(20, 0);
+   assertThat(game.getFrameScore(1)).isEqualTo(0);
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

Untitled 29.png

Cadre intégré au Bowling Game 2 (séparation des responsabilités)

Jugement d'achèvement de trame

Red

Untitled 30.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  // ry

+ @Test
+ public void test_frame_finish_two_shot() {
+   frame.recordOneShot(1);
+   assertThat(frame.isFinished()).isFalse();
+   frame.recordOneShot(1);
+   assertThat(frame.isFinished()).isTrue();
+ }
}

Green Untitled 31.png

package bowling;

public class Frame {

  private int score = 0;
+ private int shotCount = 0;

  public void recordOneShot(int pins) {
    score += pins;
+   shotCount++;
  }

  public int getScore(int frameNum) {
    return score;
  }

+ public boolean isFinished() {
+   return shotCount >= 2;
+ }
}

Jugement d'achèvement de trame (grève)

Red Untitled 32.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  // ry

+ @Test
+ public void test_frame_finish_strike() {
+   var frame = new Frame();
+   frame.recordOneShot(10);
+   assertThat(frame.isFinished()).isTrue();
+ }
}

Green Untitled 33.png

public class Frame {

  private int score = 0;
  private int shotCount = 0;

  public void recordOneShot(int pins) {
    score += pins;
    shotCount++;
  }

  public int getScore(int frameNum) {
    return score;
  }

  public boolean isFinished() {
-   return shotCount >= 2;
+   return shotCount >= 2 || score >= 10;
  }
}

Jugement de cadre intégré dans le jeu de bowling

2 points pour tous les cadres avec 1 pin pour tous les lancers

Red Untitled 34.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BowlingGameTest {

  private BowlingGame game;

  @BeforeEach
  public void setup() {
    game = new BowlingGame();
  }

  // ry

+ @Test
+ public void test_one_frame_score_is_2_when_all_one_pin() {
+   recordSamePinsManyShot(20, 1);
+   for (int frameNum = 0; frameNum < 10; frameNum++) {
+     assertThat(game.getFrameScore(frameNum + 1)).isEqualTo(2);
+   }
+ }

  private void recordSamePinsManyShot(int shotCount, int pins) {
    for (int count = 0; count < shotCount; count++) {
      game.recordOneShot(pins);
    }
  }
}

Green Untitled 35.png

import java.util.LinkedList;

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int beforePins = 0;
  private int shotCount = 1;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;
- private Frame frame = new Frame();
+ private final LinkedList<Frame> frames = new LinkedList<>() {
+   {
+     add(new Frame());
+   }
+ };

  public void recordOneShot(int pins) {
+   var frame = frames.getLast();
+   frame.recordOneShot(pins);
    totalScore += pins;
    calcSpareAddScore(pins);
    calcStrikeAddScore(pins);
    beforePins = pins;
    shotCount = shotCount == 1 && strikeAddScoreCount < 2 && doubleAddScoreCount < 2 ? 2 : 1;
+   if (frame.isFinished()) {
+     frames.add(new Frame());
+   }
  }

  public int getTotalScore() {
    return totalScore;
  }

+ public int getFrameScore(int frameNum) {
+   return frames.get(frameNum - 1).getScore();
+ }

  // ry

}

Jeter un jugement de résultat

de rechange

Red Untitled 36.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;

public class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  // ry

+ @Test
+ public void test_spare_when_defect_10_pins_in_second_shot_of_frame() {
+   frame.recordOneShot(5);
+   assertThat(frame.isSpare()).isFalse();
+   frame.recordOneShot(5);
+   assertThat(frame.isSpare()).isTrue();
+ }
}

Green Untitled 37.png

public class Frame {

  private int score = 0;
  private int shotCount = 0;

  public void recordOneShot(int pins) {
    score += pins;
    shotCount++;
  }

  public int getScore() {
    return score;
  }

  public boolean isFinished() {
    return shotCount >= 2 || score >= 10;
  }

+ public boolean isSpare() {
+   return score == 10 && shotCount >= 2;
+ }

}
la grève

Red Untitled 38.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  //ry

+ @Test
+ public void test_strike_when_defect_10_pins_in_first_shot_of_frame() {
+   assertThat(frame.isStrike()).isFalse();
+   frame.recordOneShot(10);
+   assertThat(frame.isStrike()).isTrue();
+ }
}

Green Untitled 39.png

public class Frame {

  private int score = 0;
  private int shotCount = 0;

  public void recordOneShot(int pins) {
    score += pins;
    shotCount++;
  }

  public int getScore() {
    return score;
  }

  public boolean isFinished() {
    return shotCount >= 2 || score >= 10;
  }

  public boolean isSpare() {
    return score == 10 && shotCount >= 2;
  }

+ public boolean isStrike() {
+   return score == 10 && shotCount == 1;
+ }
}

Incorporer le jugement dans le jeu de bowling

de rechange

Untitled 40.png

import java.util.LinkedList;

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;
  private final LinkedList<Frame> frames = new LinkedList<>() {
    {
      add(new Frame());
    }
  };

  public void recordOneShot(int pins) {
    var frame = frames.getLast();
    frame.recordOneShot(pins);
    totalScore += pins;
    calcSpareAddScore(pins);
    calcStrikeAddScore(pins);
    if (frame.isFinished()) {
      frames.add(new Frame());
    }
  }

  public int getTotalScore() {
    return totalScore;
  }

  public int getFrameScore(int frameNum) {
    return frames.get(frameNum - 1).getScore();
  }

  private void calcSpareAddScore(int pins) {
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
-   checkSpare(pins);
+   if (frames.getLast().isSpare()) {
+     isSpare = true;
+   }
  }

- private void checkSpare(int pins) {
-   if (shotCount == 2 && pins + beforePins == 10) {
-     isSpare = true;
-   }
- }

  private void calcStrikeAddScore(int pins) {
    addStrikeScore(pins);
    addDoubleScore(pins);
    if (isStrike(pins)) {
      recognizeStrikeAddCount();
    }
  }

  private void addStrikeScore(int pins) {
    if (strikeAddScoreCount > 0) {
      totalScore += pins;
      strikeAddScoreCount -= 1;
    }
  }

  private void addDoubleScore(int pins) {
    if (doubleAddScoreCount > 0) {
      totalScore += pins;
      doubleAddScoreCount -= 1;
    }
  }

  private boolean isStrike(int pins) {
    return pins == 10;
  }

  private void recognizeStrikeAddCount() {
    if (strikeAddScoreCount == 0) {
      strikeAddScoreCount = 2;
    } else {
      doubleAddScoreCount = 2;
    }
  }
}
la grève

Untitled 41.png

import java.util.LinkedList;

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;
  private final LinkedList<Frame> frames = new LinkedList<>() {
    {
      add(new Frame());
    }
  };

  public void recordOneShot(int pins) {
    var frame = frames.getLast();
    frame.recordOneShot(pins);
    totalScore += pins;
    calcSpareAddScore(pins);
    calcStrikeAddScore(pins);
    if (frame.isFinished()) {
      frames.add(new Frame());
    }
  }

  public int getTotalScore() {
    return totalScore;
  }

  public int getFrameScore(int frameNum) {
    return frames.get(frameNum - 1).getScore();
  }

  private void calcSpareAddScore(int pins) {
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
    }
    if (frames.getLast().isSpare()) {
      isSpare = true;
    }
  }

  private void calcStrikeAddScore(int pins) {
    addStrikeScore(pins);
    addDoubleScore(pins);
-   if (isStrike(pins)) {
+   if (frames.getLast().isStrike()) {
      recognizeStrikeAddCount();
    }
  }

  private void addStrikeScore(int pins) {
    if (strikeAddScoreCount > 0) {
      totalScore += pins;
      strikeAddScoreCount -= 1;
    }
  }

  private void addDoubleScore(int pins) {
    if (doubleAddScoreCount > 0) {
      totalScore += pins;
      doubleAddScoreCount -= 1;
    }
  }

  private void recognizeStrikeAddCount() {
    if (strikeAddScoreCount == 0) {
      strikeAddScoreCount = 2;
    } else {
      doubleAddScoreCount = 2;
    }
  }
}

(Bonus) jugement d'ajout de score / enregistrement après grève / rechange

Déplacer vers le cadre

Red Untitled 42.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  // ry

+ @Test
+ public void test_spare_add_bonus_score() {
+   frame.recordOneShot(5);
+   frame.recordOneShot(5);
+   frame.addBonusScore(5);
+   assertThat(frame.getScore()).isEqualTo(15);
+ }
}

Green Untitled 43.png

package bowling;

public class Frame {

  private int score = 0;
  private int shotCount = 0;

  public void recordOneShot(int pins) {
    score += pins;
    shotCount++;
  }

  public int getScore() {
    return score;
  }

  public boolean isFinished() {
    return shotCount >= 2 || score >= 10;
  }

  public boolean isSpare() {
    return score == 10 && shotCount >= 2;
  }

  public boolean isStrike() {
    return score == 10 && shotCount == 1;
  }

+ public void addBonusScore(int bonusScore) {
+   score += bonusScore;
+ }
}

Incorporer le jugement / l'enregistrement de l'ajout de points bonus dans le jeu de bowling

de rechange

Red Untitled 44.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  // ry

+ @Test
+ public void test_spare_frame_score_add_next_pins() {
+   game.recordOneShot(4);
+   game.recordOneShot(6);
+   game.recordOneShot(5);
+   assertThat(game.getFrameScore(1)).isEqualTo(15);
+   assertThat(game.getTotalScore()).isEqualTo(20);
+ }
}

Green Untitled 45.png

import java.util.LinkedList;

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
+ private Frame spareFrame = null;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;
  private final LinkedList<Frame> frames = new LinkedList<>() {
    {
      add(new Frame());
    }
  };

  // ry

  private void calcSpareAddScore(int pins) {
    if (isSpare) {
      totalScore += pins;
      isSpare = false;
+     spareFrame.addBonusScore(pins);
+     spareFrame = null;
    }
    if (frames.getLast().isSpare()) {
      isSpare = true;
+     spareFrame = frames.getLast();
    }
  }

  // ry
}
la grève

Red Untitled 46.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  // ry

  @Test
  public void test_strike_frame_score_add_twice_next_pins() {
    game.recordOneShot(10);
    game.recordOneShot(3);
    game.recordOneShot(4);
    game.recordOneShot(2);
    recordSamePinsManyShot(15, 0);
    assertThat(game.getTotalScore()).isEqualTo(26);
    assertThat(game.getFrameScore(1)).isEqualTo(17);
  }
}

Green Untitled 47.png

import java.util.LinkedList;

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private Frame spareFrame = null;
+ private Frame strikeFrame = null;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;
  private final LinkedList<Frame> frames = new LinkedList<>() {
    {
      add(new Frame());
    }
  };

  // ry

  private void addStrikeScore(int pins) {
    if (strikeAddScoreCount > 0) {
      totalScore += pins;
      strikeAddScoreCount -= 1;
+     strikeFrame.addBonusScore(pins);
    }
  }

  private void addDoubleScore(int pins) {
    if (doubleAddScoreCount > 0) {
      totalScore += pins;
      doubleAddScoreCount -= 1;
    }
  }

  private void recognizeStrikeAddCount() {
    if (strikeAddScoreCount == 0) {
      strikeAddScoreCount = 2;
+     strikeFrame = frames.getLast();
    } else {
      doubleAddScoreCount = 2;
    }
  }
}
Grève (2 consécutives)

Red Untitled 48.png

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FrameTest {

  private Frame frame;

  @BeforeEach
  public void setup() {
    frame = new Frame();
  }

  // ry

+ @Test
+ public void test_strike_double_frame_score() {
+   game.recordOneShot(10);
+   game.recordOneShot(10);
+   game.recordOneShot(4);
+   game.recordOneShot(2);
+   recordSamePinsManyShot(14, 0);
+   assertThat(game.getTotalScore()).isEqualTo(46);
+   assertThat(game.getFrameScore(1)).isEqualTo(24);
+   assertThat(game.getFrameScore(2)).isEqualTo(16);
+ }
}

Green Untitled 49.png

import java.util.LinkedList;

public class BowlingGame {

  private int totalScore = 0;
  private boolean isSpare = false;
  private Frame spareFrame = null;
  private Frame strikeFrame = null;
+ private Frame doubleFrame = null;
  private int strikeAddScoreCount = 0;
  private int doubleAddScoreCount = 0;
  private final LinkedList<Frame> frames = new LinkedList<>() {
    {
      add(new Frame());
    }
  };

  // ry

  private void addDoubleScore(int pins) {
    if (doubleAddScoreCount > 0) {
      totalScore += pins;
      doubleAddScoreCount -= 1;
+     doubleFrame.addBonusScore(pins);
    }
  }

  private void recognizeStrikeAddCount() {
    if (strikeAddScoreCount == 0) {
      strikeAddScoreCount = 2;
      strikeFrame = frames.getLast();
    } else {
      doubleAddScoreCount = 2;
+     doubleFrame = frames.getLast();
    }
  }
}

Continué mais en attente

Ensuite, déplacez le jugement par strikeAddScoreCount et doubleAddScoreCount vers Frame.

Même jusqu'à présent, il semble que je peux l'imaginer dans une certaine mesure, donc une fois pour l'instant (épuisé)

Image essentielle du TDD

Je ne sais pas pour ma compréhension.

Image abstraite

Tout d'abord (désolé pour l'image approximative), veuillez créer l'image suivante à partir de l'image.

―― Le cercle le plus à l'extérieur ci-dessous est la cible de développement entière, et il y a plusieurs problèmes / problèmes (cercles gris dans l'image ci-dessous) = La cible de développement est un ensemble (ensemble) de problèmes / problèmes

image.png

Sur la base de ce qui précède, lorsqu'il s'agit de procéder réellement au développement, si la compréhension de la cible est mauvaise, pour cette masse,

――Je ne sais pas par quoi commencer ――Je ne sais pas comment procéder

Je pense que ce sera. D'autre part, TDD Résolvez dans l'ordre de celui qui peut être résolu un par un de l'extérieur. Même si c'était un détour à première vue. Idéalement, l'itinéraire le plus court peut résoudre les problèmes, mais il est pratiquement impossible de résoudre les problèmes avec l'itinéraire le plus court avec certaines parties internes qui ne sont pas encore visibles. De plus, si vous pensez que c'est le plus court et essayez de résoudre plusieurs problèmes en même temps, ce sera compliqué et cela prendra plus de temps, et il vous arrivera souvent de faire un travail compliqué.

C'est pourquoi nous le décomposons en particules plus petites et résolvons les problèmes un par un.

En démêlant un à un, vous vous rapprocherez progressivement du cœur et progresserez dans votre compréhension du sujet. Au moment où vous atteindrez le cœur ou un problème proche, votre compréhension du sujet s'approfondira et vous serez en mesure de prendre les bonnes décisions concernant le problème (c'est pourquoi vous ne devriez pas décider avant d'avoir pris la bonne décision. Même si vous prenez une décision provisoire dans un état où vous ne pouvez pas encore le faire, ce sera généralement une version supplémentaire).

Lors de sa mise en œuvre, je pense que c'est ** définir / déclarer le problème à résoudre en écrivant un cas de test **.

Image de l'approche TDD

Sur la base de ce qui précède, compte tenu de l'approche TDD, TDD est

** Fixez-vous des objectifs de petite taille (objectifs à viser et problèmes à résoudre) en écrivant des cas de test, et concentrez-vous sur un seul objectif devant vous. Cela se répète. ** **

** En le faisant séparément, même conception statique et conception dynamique **

Lorsque vous en faites un, vous pouvez vous concentrer sur l'autre, et en tournant le cycle Rouge / Vert / Refactor, vous pouvez affiner la conception / mise en œuvre tout en conservant dans une certaine mesure la qualité de la conception (en fonction de la compétence du développeur). Je pense.

De plus, comme Refactor est basé sur la sécurité et la fiabilité qui peuvent être obtenues avec les éléments suivants en garantie, il est possible de se concentrer sur le travail de réflexion sur la conception dynamique.

--Compréhension / mise en œuvre de la logique nécessaire pour atteindre l'objectif

J'ai compris. c'est tout

référence

[Livre: Introduction au développement piloté par les tests TDD à partir de maintenant](https://www.amazon.co.jp/%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89%E3 % 81% AF% E3% 81% 98% E3% 82% 81% E3% 82% 8BTDD-% E3% 83% 86% E3% 82% B9% E3% 83% 88% E9% A7% 86% E5% 8B% 95% E9% 96% 8B% E7% 99% BA% E5% 85% A5% E9% 96% 80-ThinkIT-Books-% E5% 90% 89% E8% B0% B7-ebook / dp / B00VFQ7WCM / ref = cm_cr_arp_d_product_top? ie = UTF8)

[Livre: Test Driven Development](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95 % E9% 96% 8B% E7% 99% BA-% EF% BC% AB% EF% BD% 85% EF% BD% 8E% EF% BD% 94% EF% BC% A2% EF% BD% 85% EF% BD% 83% EF% BD% 8B-ebook / dp / B077D2L69C / ref = sr_1_1? __Mk_ja_JP =% E3% 82% AB% E3% 82% BF% E3% 82% AB% E3% 83% 8A & dchild = 1 & mots-clés =% E3% 83% 86% E3% 82% B9% E3% 83% 88% E9% A7% 86% E5% 8B% 95% E9% 96% 8B% E7% 99% BA & qid = 1605338667 & s = digital-text & sr = 1-1)

Recommended Posts

Maintenez l'image pratique et l'image essentielle pour lutter contre le TDD
Application Android qui sélectionne et affiche des images de la galerie
Comment affiner le format d'image de la galerie sur Android, puis sélectionner et importer plusieurs images
Comment trouver les dizaines et les unités