TDD -> Test Driven Development



Basitçe, önce birim(unit) testlerin senaryolarının yazılması, sonrasında ise kodun yazılmasıdır. Eğer testleriniz, yazdığınız kodu doğruluyorsa, tavsiyem basitçe teknik olarak yazdığınzı testlerin, kodunuzun nerelerine dokunduğuna bakın ve yüzde olarak % 65 'in altında bir kapsamı(coverage) varsa.. Yazdığınız kod için yeteri kadar test senaryolarınızın(case) olmadığını gösterir. Bu sebeple tekrar, test senaryolarınızı yazmanız kodun kalitesi ve olabilecek buglara karşı size önceden geri bildirim verir.

Sonrasında ise kodun refaktörünün basit bir kod inceleme ile (code review) yapınız ve kodu daha temiz yapınız. Tekrar test senaryolarınızı koşturunuz.. Eğer test kapsamınız yeteri yüzdeye ulaştıysa(ideal : % 100) kod , test mühendisleri için yeterli kıvama gelmiş demektir.

Örnek amaçlı küçük bir projemiz olsun.

Mesela hesap makinesinin temel aritmetik 4 işlemini yapan bir proje.

Kod java dilinde yazıyorum.


package com.calculator;

import org.junit.Test;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertThat;

public class SimpleCalculatorTest {

@Test
public void shouldReturnFiveIfParametersAreThreeAndTwoSumOrderly() {

int sum = SimpleCalculator.sum(3, 2);

assertThat(sum, equalTo(5));
}

@Test
public void shouldReturnThreeIfParametersAreFiveAndTwoSubtractOrderly() {
int result = SimpleCalculator.subtract(5, 2);

assertThat(result, equalTo(3));
}

@Test
public void shouldReturnTenIfParametersAreFiveAndTwoMultiply() {
int result = SimpleCalculator.multiply(5, 2);

assertThat(result, equalTo(10));
}

@Test
public void shouldReturnTwoIfParametersAreFourAndTwoDivide() {
int result = SimpleCalculator.divide(4, 2);

assertThat(result, equalTo(2));
}

}



Test senaryolarımı oluşturdum, ve senaryoları oluştururken SimpleCalculator sınfımdaki metod isimlerine karar verdim ve ilgili sınıfta oluşturdum. İlk aşamada SimpleCalculator sınıfı aşağıdaki gibi..

package com.calculator;

/**
* Created by turgaycan on 4/17/15.
*/
public class SimpleCalculator {

public static int sum(int firstParam, int secondParam) {
return 0;
}

public static int subtract(int firstParam, int secondParam) {
return 0;
}

public static int multiply(int firstParam, int secondParam) {
return 0;
}

public static int divide(int firstParam, int secondParam) {
return 0;
}
}



Testlerimi çalıştırıyorum.. Sonuç aşağıdaki gibi..

java.lang.AssertionError:
Expected: <3>
but: was <0>

at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:865)
at org.junit.Assert.assertThat(Assert.java:832)
at com.calculator.SimpleCalculatorTest.shouldReturnThreeIfParametersAreFiveAndTwoSubtractOrderly(SimpleCalculatorTest.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)


java.lang.AssertionError:
Expected: <5>
but: was <0>

at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:865)
at org.junit.Assert.assertThat(Assert.java:832)
at com.calculator.SimpleCalculatorTest.shouldReturnFiveIfParametersAreThreeAndTwoSumOrderly(SimpleCalculatorTest.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)


java.lang.AssertionError:
Expected: <2>
but: was <0>

at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:865)
at org.junit.Assert.assertThat(Assert.java:832)
at com.calculator.SimpleCalculatorTest.shouldReturnTwoIfParametersAreFourAndTwoDivide(SimpleCalculatorTest.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)


java.lang.AssertionError:
Expected: <10>
but: was <0>

at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:865)
at org.junit.Assert.assertThat(Assert.java:832)
at com.calculator.SimpleCalculatorTest.shouldReturnTenIfParametersAreFiveAndTwoMultiply(SimpleCalculatorTest.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)


Process finished with exit code 255



Tüm testler patladı..

Devam ediyoruz.. Şimdi kod yazma zamanı ;)

package com.calculator;

/**
* Created by turgaycan on 4/17/15.
*/
public class SimpleCalculator {

public static int sum(int firstParam, int secondParam) {
return firstParam + secondParam;
}

public static int subtract(int firstParam, int secondParam) {
return firstParam - secondParam;
}

public static int multiply(int firstParam, int secondParam) {
return firstParam * secondParam;
}

public static int divide(int firstParam, int secondParam) {
return firstParam / secondParam;
}
}



Test sonuçları;

All Tests Passed!


Kod basit olduğu için refactoring(refaktör) olayını parametre isimleri ile yapabiliriz aslında.

Şimdi daha anlamlı parametre isimleri verelim.

Bölme işleminde parametre isimlerini değiştirdim. Aşağıdaki gibi daha anlamlı oldu.


public static int divide(int divided, int divider) {
return divided / divider;
}



IntellijIDEA IDE'nin bir rahatlığıyla, test coverage'a baktım sınıfın: %100, line(satır) olarak : % 80

Sevgilerle,

Daha detaylı bilgi için : wiki