Best Practice JUnit: Wie Klassen durch Stubs/Mockups ersetzen

  • Themenstarter Themenstarter trez
  • Beginndatum Beginndatum
T

trez

Gast
Hallo

Wie führe ich Unittests einer Klasse durch, welche andere Klassen verwendet, welche ich aber durch Fake-Klassen ersetzen will?

Ich möchte ja nicht bei jedem Test manuell Dateien ersetzen und die ersetzen. Die Fake-Klassen sind möglicherweise nicht bei allenTests dieselben Dateien, aber auch das ist kein Muss, das kann ich auch anders lösen

Wie macht ihr das konkret?
Per Script alles was man brauch zusammenkopieren?
Wie Fake ich Klassen die nicht mir gehören? (z.B. File ...)

Unten ein extrem abgespecktes Beispiel, das mit Fakes von ClassOne und ClassThree gestestet werden soll.

Java:
package test.package2;

import test.package1.ClassOne;
import test.package3.ClassThree;

public class ClassTwo {

	ClassOne c1;
	ClassThree c3;

	public ClassTwo() {
		c1 = new ClassOne();
		c3 = new ClassThree();
	}
	
	public void doIt() {
		if (c1.getFlag()) {
			c3.setValue(42);
		} else {
			c3.setValue(24);
		}
	}
}

Google-Stichworte?
Links auf Beschreibungen?

Besten Dank
trez
 
Danke - ich bin am lesen. Ich kann den Aufwand noch nicht abschätzen und sehe auch noch nicht so genau wie ein Framework die Build-Pfade "umbiegen" kann. (Die Originalklassen sind bei uns hier auch im Build-Pfad)

Gibt es als Alternative möglicherweise ein Lösung per Script (Ant? Maven?) vor Testbeginn die Dateien zu kopieren?
(Da bin ich auch nicht der Guru)

[Edit] Mockito scheint eine Schicht zwischen die UUT und den verwendeten Klassen zu legen und das wäre nicht das, was ich möchte. Ich möchte, dass zur Testzeit eine anderer Sourcecode von ClassOne und ClassThree verwendet wird.

Nach einer halben Stunde lesen bin ich zur erwähnten Erkenntnis gelangt - gegen mögliche Blindheit hilft vielleicht ein Zaunpfahl
 
Zuletzt bearbeitet von einem Moderator:
Meine Erkenntnisse nach dem Wochenende:

Mockito erzeugt eine Klasse nach einem Interface, welche der UUT übergeben wird.
So wird es wenigstens in den Beispielen gemacht, die ich gefunden habe.

Ich habe keine Interfaces (könnte ich zwar ändern) und die Instanzen werden direkt in der UUT erzeugt.

Was gibt es für einen Weg den Code der verwendeten Klassen für die Tests zu ersetzen?
 
Du kannst deine realen Klassen von aussen reinreichen ("Injizieren") Dazu solltest du deinen Code "testbarer" machen😉
Java:
public class TestClassTwo {

    @Test
    public void test() {
	ClassTwo classTwo = new ClassTwo(new FakeClassOne(), new FakeClassThree());
	classTwo.doTest();
    }

}

class ClassOne {

}

class ClassThree {

}

class ClassTwo {
    private ClassOne classOne;
    private ClassThree classThree;

    public ClassTwo(ClassOne classOne, ClassThree classThree) {
	this.classOne = classOne;
	this.classThree = classThree;

    }
}

Damit kannst du es einem DI-Framework überlassen, die Abhängigkeiten aufzulösen (Spring, Guice,...) oder wie hier selbst im Test machen.
 
Zuletzt bearbeitet:
Also wie turtle schon gesagt hat, du musst deine Klasse testbarer machen. Er hat dir auch schon ein Bsp. gegeben, dass du zum Beispiel einen Konstruktor mehr machst und die zwei Objekte mitgibst. In deiner Konfiguration ist es nicht möglich das Verhalten von deinen zwei internen Objekten direkt zu testen.
 
Also um das zu konkretisieren habe ich dir dein Beispiel mit EasyMock aufbereitet:

TestClassTwo.java
Java:
package mock;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;

import org.easymock.EasyMockRunner;
import org.easymock.EasyMockSupport;
import org.easymock.Mock;
import org.easymock.TestSubject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(EasyMockRunner.class)
public class TestClassTwo extends EasyMockSupport {
	
	@Mock ClassOne classOne;
	@Mock ClassThree classThree;
	
	@TestSubject
	ClassTwo classTwo;

	@Before
	public void setUp() {
		classTwo = new ClassTwo(classOne, classThree);
	}
	
	@After
	public void tearDown() {
		classTwo = null;
	}
	
	@Test
	public void testFlagTrue() {
		expect(classOne.getFlag()).andStubReturn(true);
		classThree.setValue(42);
		replayAll();
		classTwo.doIt();		
		assertTrue(classOne.getFlag());
	}
	
	@Test
	public void testFlagFalse() {
		expect(classOne.getFlag()).andStubReturn(false);
		classThree.setValue(24);
		replayAll();
		classTwo.doIt();
		assertFalse(classOne.getFlag());
	}
}

ClassOne.java
Java:
package mock;

public class ClassOne {
	
	private boolean flag;
	
	public ClassOne() {
		flag = false;
	}
	
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	
	public boolean getFlag() {
		return flag;
	}
}

ClassTwo.java

Java:
package mock;

public class ClassTwo {
	
	private ClassOne classOne;
	private ClassThree classThree;
	
	public ClassTwo() {
		classOne = new ClassOne();
		classThree = new ClassThree();
	}
	
	public ClassTwo(ClassOne classOne, ClassThree classThree) {
		this.classOne = classOne;
		this.classThree = classThree;
	}
	
	public void doIt() {
		if(classOne.getFlag()) {
			classThree.setValue(42);
		}
		else {
			classThree.setValue(24);
		}
	}
}

ClassThree.java

Java:
package mock;

public class ClassThree {
	private int value;
	
	public ClassThree() {
		value = 0;
	}
	
	public void setValue(int value) {
		this.value = value;
	}
	
	public int getValue() {
		return value;
	}
}
 
Zuletzt bearbeitet:

Zurück
Oben