Tagged: testing RSS Toggle Comment Threads | Keyboard Shortcuts

  • Chris Kaempfe 9:40 am on December 9, 2011 Permalink | Reply
    Tags: , , testing   

    JUnit Tests und Reflection 

    Mit Hilfe von Java Reflection lassen sich grosse POJO Objekte unter JUnit einfach Vergleichen.

    Angenommen man hat ein einfaches POJO Objekt Metadata mit einigen Attributen:

    public class Metadata {
        private Integer id;
        private String filename;
        private Integer height;
        private Integer width;
        private long fileSize;
        private String fileFormat;
        private String comment;
        // and some more attributes
    
        /**
         * Getter and Setter-Methods
         */
    }
    

    Es werden nie alle Attribute gleichzeitig gefüllt, sondern je nach Prozessschritt immer nur ein paar. Zum Beispiel beim Einlesen der Datei können Breite, Höhe, Dateiname und Dateigröße gesetzt werden. Alle anderen Attribute sollen nicht gefüllt werden. Um sicherzustellen, dass beim Einlesen der Datei auch keine weiteren Attribute gesetzt werden, muss im Test abgefragt werden, ob alle anderen Attribute auch NULL sind.

    @Test
    public void testReadFileMetadata() {
        Metadata meta = new Metadata();
        File file = new File("someTestFile.jpg");
        // Metadata attributes will be read and set
        foo.readFileMetadata(meta, file);
        // now test if attributes were set correct
        assertEquals("someTestFile.jpg", meta.getFileName());
        // ... go on with height, width and size
        // all other attributes should still be NULL
        assertNull(meta.getFileFormat());
        assertNull(meta.getId);
        assertNull(meta.getComment);
    }
    

    Die Id wird dem Metaobjekt zum Beispiel über eine Datenbank hinzugefügt. Hierzu gibt es eine eigene Methode createMetaId():

    public void createMetaId(Metadata meta) {
        // set up database stuff
        Integer id = database.getNextId();
        meta.setId(id)
    }
    

    Für einen vollständigen Test, müssten auch hier wieder alle Attribute abgefragt werden:

    @Test
    public void testCreateMetaId() {
        Metadata meta = new Metadata();
        dbTier.createMetaId(meta);
        assertEquals(123456, meta.getId()):
        // test if all other attributes are NULL
        assertNull(meta.getFileFormat());
        // ...
        assertNull(meta.getComment);
    }
    

    Unter Verwendung von Reflection kann man bei den Tests sich eine Menge an Code sparen:

    public class ReflectionComparator {
    	public static void compareObjects(Object expected, Object actual) {
    		try {
    			for(Field field : expected.getClass().getDeclaredFields()) {
    				// setAccessible == true will allow to access private fields
    				field.setAccessible(true);
    				assertEquals(field.get(expected), field.get(actual));
    			}
    		} catch(Exception e) {
    			fail(e.getMessage());
    		}
    	}
    }
    

    Die Tests angepasst mit der Klasse ReflectionComparator sehen wie folgend aus:

    @Test
    public void testReadFileMetadata() {
        Metadata metaActual = new Metadata();
        File file = new File("someTestFile.jpg");
        // Metadata attributes will be read and set
        foo.readFileMetadata(metaActual, file);
        // create new object Metadata and set the expected attributes
        Metadata metaExpected = new Metadata();
        metaExpected.setFileName("someTestFile.jpg");
        // ... go on with setters for height, width and size
        // compare metaExpected with metaActual
        ReflectionComparator.compareObjects(metaExpected, metaAcutal);
    }
    
    @Test
    public void testCreateMetaId() {
        Metadata metaActual = new Metadata();
        dbTier.createMetaId(metaActual);
        Metadata metaExpected = new Metadata();
        metaExpected.setId(123456);
        // compare metaExpected with metaActual
        ReflectionComparator.compareObjects(metaExpected, metaAcutal);
    }
    

    Damit hat man sich jede Menge an Testcode gespart und überprüft zusätzlich alle Attribute eines Objektes, ob diese gleich sind.

     
  • Chris Kaempfe 2:32 pm on November 14, 2011 Permalink | Reply
    Tags: , , testing   

    Mockito und Byte Arrays 

    Der Aufruf von Methoden mit Byte-Arrays ist mit Mockito kein grosses Problem. Um ein Byte-Array zu matchen wird nicht, wie für die ‘Standard’-Argumente, die Matcher-Klasse verwendet, sondern die AddiationalMatchers. Die AdditionalMatchers können für alle primitiven Arrays von Java verwendet werden.

    Ein kleines Beispiel um dies zu verdeutlichen:

    // handles several types for some purpose
    public class TypeHandler() {
        public void byteMethod(byte[] array) {
            // do something here with byte array
        }
    }
    

    Die von Mockito zu verifizierende Methode byteMethod hat die oben gezeigte Signatur und es soll getestet werden, ob die Methode auch einmal mit dem entsprechenden Byte-Array aufgerufen wird:

    public void testByteMethod() {
        byte[] data = new byte[7];
        // create Mock
        TyepHandler typeHandlerMock = mock(TypeHandler.class);
        Foo foo = new Foo();
        foo.setTypeHandler(tyepHandlerMock);
        // within this method the 'byteMethod' should be called once
        foo.runByteMethod();
        verify(typeHandlerMock).byteMethod(AdditionalMatchers.aryEq(data));
    }
    

    Mit Hilfe des AdditionalMatchers und der Methode aryEq wird überprüft, ob auch das erwarte Byte-Array an die Methode übergeben wird. Das ganze kann man aber auch allgemeiner halten:

        verify(typeHandlerMock).byteMethod(any(byte[].class));
    

    Nun überprüft Mockito, ob die Methode byteMethod mit irgend einem Byte-Array aufgerufen wird.

     
  • Chris Kaempfe 10:00 am on November 9, 2011 Permalink | Reply
    Tags: , , testing   

    Parametrisiert Testen 

    Es gibt Situationen beim Testen von Methoden die recht anstrengend sein können, beispielsweise wenn man diverse verschiedene Aufrufparameter für diese haben kann. Hier kann man sehr gut mit parametrisierten Test arbeiten. D.h. man ruft ein und dieselbe Testmethode auf, ‘stöpselt’ jedoch jedesmal andere Werte der Aufrufparameter ein.

    Das folgende Beispiel soll eine Modulo 2 Methode testen. Ist zwar sehr einfaches Beispiel sollte jedoch gut veranschaulichen wozu man parametrisierte Tests verwenden kann.
    Zuerst baut man sich ein Grundgerüst für seinen Datencontainer:

    @SuppressWarnings("rawtypes")
    @Parameters
    public static Collection data()
    {
    	Object[][] data = new Object[][] {
    		// Modulo 1
    		{
    			// expected result
    			1,
    			// value for parameter
    			1
    		},
    		// Modulo 2
    		{
    			// expected result
    			0,
    			// value for parameter
    			2
    		}
    	};
    	return Arrays.asList(data);
    }
    

    Im Datencontainer definierten wir uns ein erwartes Ergebnis zu dem entsprechenden Wert des Aufruf-Parameters. So erwarten wir zum Beispiel für 1 mod 2 = 1 und für 2 mod 2 = 0

    Damit wir auf die Werte zugreifen können, werden diese über den Konstruktor unserer Testklasse eingestöpselt:

    private int expectedResult;
    private int paramValue;
    public ModuloParameterizedTest(int expectedResult, int paramValue) {
    	this.expectedResult = expectedResult;
    	this.paramValue = paramValue;
    }
    

    Nun können wir mit den Werten in unserer Testmethode arbeiten:

    @Test
    public void testModuloTwo() {
    	Modulo modulo = new Modulo();
    	assertEquals(expectedResult, modulo.moduloTwo(paramValue));
    }
    

    Die Methode moduloTwo soll die Aufgabe paramValue mod 2 durchführen und das Ergebnis zurückliefern. Das Ergebnis wird dann mit unserem erwarteten Ergebnis verglichen und sofern dies korrekt ist, ist der Test erfolgreich bestanden. JUnit führt den Aufruf der testModuloTwo-Methode genau zweimal durch, da wir im Datencontainer zwei Fälle definiert haben.

    Abgesehen davon, dass es für Modulo unendlich viele Testmöglichkeiten gibt, zeigt es wie man einen parametrisierten Test aufsetzt. Zum Schluss noch einmal den ganzen Code am Stück:

    import static org.junit.Assert.assertEquals;
    
    import java.util.Arrays;
    import java.util.Collection;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.Parameterized;
    import org.junit.runners.Parameterized.Parameters;
    
    @RunWith(value = Parameterized.class)
    public class ModuloParameterizedTest {
    @SuppressWarnings("rawtypes")
    @Parameters
    	public static Collection data()
    	{
    		Object[][] data = new Object[][] {
    			// Modulo 1
    			{
    				// expected result
    				1,
    				// value for parameter
    				1
    			},
    			// Modulo 2
    			{
    				// expected result
    				0,
    				// value for parameter
    				2
    			}
    		};
    		return Arrays.asList(data);
    	}
    	// variables to store the data
    	private int expectedResult;
    	private int paramValue;
    
    	public ModuloParameterizedTest(int expectedResult, int paramValue) {
    		this.expectedResult = expectedResult;
    		this.paramValue = paramValue;
    	}
    
    	@Test
    	public void testModuloTwo() {
    		Modulo modulo = new Modulo();
    		assertEquals(expectedResult, modulo.moduloTwo(paramValue));
    	}
    }
    

    Damit JUnit weiß, dass wir die Klasse parametrisieren wollen, benötigen wir noch die Annotation @RunWith(value = Parameterized.class) für unsere Testklasse.

     
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
l
go to login
h
show/hide help
shift + esc
cancel