Versionen im Vergleich

Schlüssel

  • Diese Zeile wurde hinzugefügt.
  • Diese Zeile wurde entfernt.
  • Formatierung wurde geändert.

...

Codeblock
languagejava
title/<extension-server>/src/test/java/de/fdm/server/explorer/PdmcaeExplorerFacadeBeanTest.java
[...] // siehe oben

    @BeforeClass(dependsOnMethods={"springTestContextPrepareTestInstance"})
    public void setupParamValidation(){
        // Test class setup code with autowired classes goes here
    }
    
    @AfterTest(alwaysRun=true)
    public void afterTest() {
        reset(masterDataFacade);
    }     

Ein Beispiel für Boilerplate Code, den man recht oft benötigt. Die Method setupParamValidation hat Platz vor Code, der nach der Spring Context Erzeugung und vor allen Tests ausgeführt werden soll. Wichtig ist hier nicht der Methodenname, sondern die Annotierung. Diese Methode hat für den TestNG ungefähr die Funktion, die eine mit @PostConstruct annotierte Methode für Spring Beans hat.

Mock Objekte muss man nach jeden Tests auf den Ausgangszustand zurücksetzen, damit Problem, die sich in einem Test ergeben, nicht in den danach ausgeführten Test propagieren. In TestNG gelingt dies mit der Annotation @AfterTest(alwaysRun=true). Typischerweise wird das reset von Mockito für die Mock Objekte aufgerufen, die mittels Spring injiziert wurden.

Typischer Test

Codeblock
languagejava
title/<extension-server>/src/test/java/de/fdm/server/explorer/PdmcaeExplorerFacadeBeanTest.java
[...] // siehe oben

    @Test
    public void testGetSubNodesForRootSubnodes1() throws CommonBusinessException {
        // u.U. wegen afterTest Method nicht notwendig
        reset(masterDataFacade);
        
        // prepare input data
        final MasterDataVO<Long> sub1 = dataFactory.getBO_Fahrzeugbaum(null, "sub1");
        final Long sub1Id = sub1.getPrimaryKey();
        final PdmcaeTreeNode sub1Node = dataFactory.asPdmcaeTreeNode(sub1);
        final MasterDataVO<Long> sub2 = dataFactory.getBO_Fahrzeugbaum(null, "sub2");
        final Long sub2Id = sub2.getPrimaryKey();
        final PdmcaeTreeNode sub2Node = dataFactory.asPdmcaeTreeNode(sub2);
        final MasterDataVO<Long> subsub = dataFactory.getBO_Fahrzeugbaum(sub2, "subOfSub2");
        final Long subsubId = subsub.getPrimaryKey();
        final PdmcaeTreeNode subsubNode = dataFactory.asPdmcaeTreeNode(subsub);
        
        final PdmcaeRootTreeNode root = PdmcaeRootTreeNode.getInstance();
        root.setSearchResultSubnodeIds((Set) dataFactory.set(sub1Id, null, sub2Id));
        
        // prepare mocks
        // for getSubnodesByIds
        when(masterDataFacade.getMasterData(eq(BO_Fahrzeugbaum), (CollectableSearchCondition) any(), eq(true))).thenReturn(
                (TruncatableCollection) TruncatableCollectionDecorator.createUntruncated(dataFactory.list(sub1, sub2)));
        
        // Method to test
        final List<TreeNode> result = dut.getSubNodes(root);
        LOG.info("getSubNodesForRootSubnodes1: result is " + result);
        
        // return value verification
        assertEquals(result.size(), 2);
        assertTrue(result.contains(sub1Node));
        assertTrue(result.contains(sub2Node));
        assertFalse(result.contains(subsubNode));
        
        // mock verification
        verify(masterDataFacade).getMasterData(eq(BO_Fahrzeugbaum), (CollectableSearchCondition) any(), eq(true));
        verifyNoMoreInteractions(masterDataFacade);
    }

Der eigentliche Unit Test gliedert sich in folgende Teile:

  1. @Test Annotierung
  2. reset Mock Objekte (optional)
  3. Aufbau der/des Input Value Object(s)
  4. Definition, was von den Mock Objekten zur Laufzeit erwartet wird (d.h. Definition der erwarteten Methodenaufrufe und deren Rückgabewerte)
  5. Aufruf der zu testenden Methode
  6. Verifikation des Ergebnisses (d.h. des Rückgabewertes und der Änderungen an den Input Value Objects)
  7. Verifikation der Mock Objekte

An diesem einfachen Testfall kann man bereits erkennen, das er Aufruf der zu testenden Methode nur ein ganz kleiner Teil des Unit Testes ist. Den größten Teil eines Tests nimmt meist die Behandlung der Mock Objekte in Anspruch. Es ist daher notwendig, sich mit dem verwendeten Mock Framework gut auszukennen.