Testdesignteknik: Ækvivalenspartitionering

Prøv at sige det hurtigt 5 gange! Det fornemme ord henviser til en logisk måde, hvorpå du kan inddele værdier i grupper. De fleste bruger den allerede uden at tænke over det. Når det så er sagt, så vil du opleve, at det bliver lettere at udvælge relevante input værdier, når du bevidst tager denne teknik i brug.
  Ækvivalenspartitionering handler som sagt om at opdele input i grupper (partitioner). Hver gruppe forventes at have samme opførsel og blive behandlet på samme måde i applikationen. Grupperne kan findes for:

  • Output værdier (indenfor/udenfor kreditgrænse)
  • Valide/invalide værdier (bestillingsmængder min/max)
  • Tidsrelaterede værdier (før, under og efter en opgave)
  • Sæt af muligheder (hårfarve, øjenfarve)

Når du designer test med denne teknik, anvender du mindst én værdi til at repræsentere hver partition. I teorien er én værdi fra hver partition nok, men der kan være tilfælde, hvor du af forretnings logiske grunde gerne vil anvende flere, eller når du arbejder med grænseværdianalyse, som vi kommer til senere.

When it all comes together...


Ex. Vurdering af elev ud fra karakter
Lad os tage et simpelt eksempel og sige, at du har en metode, der skal returnere, hvorvidt eleven er bestået eller ej ud fra hans eller hendes karakter. Vores forretningslogik siger, at karaktererne -3, 00 og 02 betyder at eleven er dumpet. 4, 7, 10 og 12 betyder, at eleven har bestået.
  Kravspecifikationen siger, at brugerne skal kunne angive en karakter og få en besked tilbage om, hvorvidt eleven er bestået eller ej. Ydermere blev man på det sidste udviklingsmøde enige om at logge alle karakterer med ugyldige værdier.

Trin 1

Først opretter jeg min klasse og min metode, der i første omgang kun skal returnere en tom streng:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class StudentEvaluation
{
    private ILogService _logService;

    public StudentEvaluation(ILogService logService)
    {
        this._logService = logService;
    }

    public string HasPassed(int grade)
    {
        return string.Empty;
    }
}

Trin 2

Uden at bruge nogen som helst teknik eller strategi kunne vi teste vores metode med alle syv karakterer, da det trods alt er et overskueligt antal. Men hvis vi fx havde 70 eller 700 karakterer ville det være en helt anden sag. Ækvivalenspartitionering kan bruges uanset om du har 7, 70 eller flere mulige input værdier. Hvis jeg skulle opdele de mulige input værdier i partitioner ville jeg sige, at jeg har fire partitioner: ‘bestået’, ‘ikke bestået’, ‘gyldig’ og ‘ugyldig’.

Ugyldig
Ikke bestået
Bestået
Ugyldig
-4
-3
00
02
4
7
10
12
13

Jeg skriver mine unit tests ud fra mine partitioner. For mig resulterer det i tre unit tests; ‘bestået’, ‘ikke bestået’ og ‘ugyldig’. Jeg laver bevidst ikke en unit test for partitionen ‘gyldig’, da denne er dækket af både ‘bestået’ og ‘ikke bestået’.
  Jeg bruger desuden AAA mønstret i mine unit tests. AAA mønstret (arrange, act og assert) hjælper dig med at strukturere dine unit tests og understøtter et godt testdesign. Evt. input værdier og startbetingelser placeres under arrange, og forventede resultater verificeres under assert.
  Bemærk også at jeg bruger Moq til at mocke min log komponent ved hjælp af det interface, som min log komponent implementerer. Det betyder, at teamets reelle log komponent aldrig kaldes, og at mine unit tests derfor aldrig vil skabe rod i vores log. Som en ekstra bonus kan jeg verificere, at log komponenten kun bliver kaldt, som jeg forventer det og kun med den forventede besked.

Altså får jeg nedenstående unit tests:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    [TestClass]
    public class Tests_StudentEvaluation
    {
        [TestMethod]
        public void Tests_HasPassed_Passed()
        {
            //Arrange
            int grade = 10;
            string expectedResult = "Passed";
            var mockLog = new Mock<ILogService>();

            //Act
            StudentEvaluation evaluation = new StudentEvaluation(mockLog.Object);
            string actualResult = evaluation.HasPassed(grade);

            //Assert
            Assert.IsTrue(expectedResult.Equals(actualResult), "The student didn't pass as expected.");
            mockLog.VerifyNoOtherCalls();
        }

        
        [TestMethod]
        public void Tests_HasPassed_Not_Passed()
        {
            //Arrange
            int grade = 0;
            string expectedResult = "Failed";
            var mockLog = new Mock<ILogService>();

            //Act
            StudentEvaluation evaluation = new StudentEvaluation(mockLog.Object);
            string actualResult = evaluation.HasPassed(grade);

            //Assert
            Assert.IsTrue(expectedResult.Equals(actualResult), "The student didn't fail as expected.");
            mockLog.VerifyNoOtherCalls();
        }

        
        [TestMethod]
        public void Tests_HasPassed_Invalid_Grade()
        {
            //Arrange
            int invalidGrade = 13;
            string expectedResult = string.Empty;
            var mockLog = new Mock<ILogService>();
            string expectedLogMessage = $"An attempt to evaluate the invalid grade {invalidGrade} was made.";

            //Act
            StudentEvaluation evaluation = new StudentEvaluation(mockLog.Object);
            string actualResult = evaluation.HasPassed(invalidGrade);

            //Assert
            Assert.IsTrue(expectedResult.Equals(actualResult), "Invalid grade wasn't handled as expected.");
            mockLog.Verify(log => log.Log(expectedLogMessage), "The expected message wasn't logged.");
            mockLog.VerifyNoOtherCalls();
        }
    }

Trin 3

Da jeg kørte min unit test fejlede de som forventet. Herefter ville jeg kunne fortsætte med at implementere min metode HasPassed, indtil mine unit tests bestod og derefter lave refactoring af min kode.

Alt kode i denne serie af indlæg kan findes på min Github profil:

Fortsættes...

Del 12: Testdesignteknik: Ækvivalenspartitionering

Ingen kommentarer:

Send en kommentar