Testowanie kodu

(naprawdę wszyscy to robią ;)

Po co


  • Po co zastanawiać się czy kod który napisałeś(aś) jest poprawny?

  • Po co zastanawiać się czy zmiany nie zepsuły niczego innego?
  • Inne argumenty.


Testy pozwalają szybko sprawdzić działanie programu.

Sprężyste zderzenia w (prawie) 1d

 public class Ball {

private double[] coords = new double[6];

public Ball(double x, double y, double vx,
double vy, double mass, double radius){
coords = new double[]{x, y, vx, vy, mass, radius};
}

public void iteration(double dt){
coords[0]+=coords[2]*dt;
coords[1]+=coords[3]*dt;
}

Testy

 public class BallTests {

@Test
public void testMove(){
Ball ball = new Ball(0, 0, 1, 2, 5, 1);
ball.iteration(0.1);
Assert.assertEquals(ball.getX(), 0.1, 0.001);
Assert.assertEquals(ball.getY(), 0.2, 0.001);
}

Wyjaśnienie

  • @Test jest adnotacją (wyjaśnienie, tutorial)
  • Informuje ona Framework JUnit że dana metoda jest metodą
    testową
  • Taka metoda zostanie automatycznie wykonana podczas uruchamiania testów jednostkowych
  • Test zostaje uznany za "zaliczony pozytywnie" jeśli zakończył się bez zgłaszania wyjątku
  • Można sprawdzić różne zachowania programu za pomocą funkcji Assert.

Uruchomienie testów

Uruchamianie testów 2


Wyniki


  • Wielki zielony napis że wszystko OK!

Klasa z metodami użytkowymi

 public class BallUtils{

public static double distance(Ball b1, Ball b2){
return Math.sqrt(
Math.pow(
b1.getX()-b2.getX(), 2
) +
Math.pow(
b1.getY()-b2.getY(), 2
)
);
}

public static boolean overlaps(Ball b1, Ball b2){
return distance(b1, b2) <= (b1.getRadius() + b2.getRadius());
}
}


Testy

 public class BallTests { ...

@Test
public void testDistance1(){
double distance = BallUtils.distance(
ballFromPositions(0, 0),
ballFromPositions(0, 1)
);
Assert.assertEquals(1.0, distance, 0.0001);
}

...

@Test
public void testOverlaps(){
Assert.assertTrue(BallUtils.overlaps(
ballFromPositions(0, 0, 1),
ballFromPositions(0, 0.5, 1)
));
}


Cykl życia testów

 public class Engine {

private ArrayList<Ball> balls = new ArrayList<Ball>();

public void addBalls(Ball b){
balls.add(b);
}

public List<Ball> getBalls(){
return Collections.unmodifiableList(balls);
}

public void iterate(double dt){
for (int ii=0;ii<balls.size(); ii++){
Ball b1 = balls.get(ii);
for (int jj=ii+1; jj<balls.size(); jj++){
Ball b2 = balls.get(jj);
if (BallUtils.overlaps(b1, b2)){
b1.collision(b2);
}
}
b1.iteration(dt);
}
}
}

Cykl życia testów

 public class CollisionTest{

Engine engine;

@Before
public void setUp(){
engine = new Engine();
engine.addBalls(new Ball(0, 0, 0, 1, 0, 1));
}

@Test
public void testNoCollision(){
engine.addBalls(new Ball(0, 3, 0, -2, 0, 1));
engine.iterate(0.1);
Assert.assertEquals(engine.getBalls().get(0).getVY(), 1, 0.01);
Assert.assertEquals(engine.getBalls().get(1).getVY(), -2, 0.01);
}

@Test
public void testCollision(){
engine.addBalls(new Ball(0, 2, 0, -2, 0, 1));
engine.iterate(0.1);
Assert.assertEquals(engine.getBalls().get(0).getVY(), -2, 0.01);
Assert.assertEquals(engine.getBalls().get(1).getVY(), 1, 0.01);
}

}

Cykl życia testów

  • Test to każda metodą z adnotacją @Test
  • przed każdym testem wykonywana jest metoda z adnotacją @Before
  • po każdym teście wykonywalna jest metoda z adnotacją @After

Znajdź błąd

Zasady pisania testów

  • Działanie testu A nie może zależeć od działania innych testów
  • Każda ścieżka wykonania kodu powinna mieć jeden test

Jak dodać biblioteki unittestów


  • Niestety biblioteka junit nie jest w JDK (trzeba ją oddzielnie pobrać)
  • Na szczęście można ją bardzo łatwo dodać do projektu!


Można też dodać do projektu

Przykładowy projekt

Testowanie kodu

By Jacek Bzdak

Testowanie kodu

  • 2,004