# JUnit Tests in Jakarta EE und Wildfly



## beta20 (11. Nov 2020)

Hallo zusammen, 

ich würde gerne mir ein paar Junit / Arquillian Tests schreiben um meine EJB - Klassen in meinem Webprojekt zu testen. Ich denke die Frage ist relativ einfach, nur happert es an den Einstellungen, die ich tätigen muss.
Die Idee ist, dass ich in meiner Test - Klasse dann eben mein EJB Interface instanziere und dann die Methoden aufrufen kann. 

Ich habe schon einiges an Internetrecherche betrieben, aber so wirklich fündig geworden bin ich nicht bzw. als ich etwas ausprobiert habe, habe ich verschiedene Fehler bekommen, daher frage ich hier nach Rat.

Zu meinen Gegebenheiten:
- Ich verwende Wildfly 21
- Jakarta 8
- Maven
- Eclipse als IDE

Folgende Dinge würden mir enorm helfen:
1) Was muss ich in meiner pom.xml anpassen?
2) Wie führe ich die Tests in der IDE aus? Geschieht das mit "Maven test" ? Oder Run As "Junit Test" ? (Screenshot wäre super)
3) Code einer Beispiel Testklasse (inkl. aller imports)

Für Hilfe wäre ich sehr dankbar.


----------



## mrBrown (11. Nov 2020)

Brauchst du nur einfach Unit-Tests oder brauchst du Integration-Tests mit laufendem Wildfly?


----------



## beta20 (11. Nov 2020)

Worin besteht denn genau der Unterschied? 

Für´s Erste reichen mir wohl erst mal Unit Tests, sodass ich Methode XY aufrufen kann und bspw. mit den Parameter in der Testklasse befülle und am Ende kommt dann eben Ergebnis (true oder false) heraus. Nur hierfür benötige ich doch ebenfalls einen laufenden Wildfly?
Mein Wildfly läuft ja derzeit sowieso. 
Aber wenn ich das richtig verstehe, dann wird speziell für das Testen nochmal eine eigene Instanz von Wildfly gestartet?

Falls dem nicht so ist, denke ich früher oder später benötige ich auch Tests mit laufendem Wildfly.


----------



## LimDul (11. Nov 2020)

Man braucht nicht zwangsweise einen Wildfly. Man kann auch einen BeanManager von Hand für Tests erzeugen und da die Beans reinstecken, die gefunden werden sollen. So machen wir das für Tests. Wir nutzen im Wildfly apache Deltaspike und haben dann einen TestBeanManager als Implementierung von BeanManagerImpl mit etwas Wrapper Funktionalität drum rum. Das reicht bei uns für 90% der Anwendungsfälle.


----------



## beta20 (11. Nov 2020)

Danke Dir.

Das würde mir auch erstmal reichen....
Hast Du denn ein Sample Code? Was muss ich in der pom.xml einstellen usw. ? Welches jars benötige ich?


----------



## mrBrown (11. Nov 2020)

beta20 hat gesagt.:


> Worin besteht denn genau der Unterschied?


Unit-Tests testest "Units" in Isolation, ohne die durch die JakartaEE-Container Bereitgestellte Funktionalität.
Integration Tests testen die Integration des ganzen, also wie das ganze im Container läuft.

Erstere sind schnell und einfach, letztere komplexer, langsamer und brauchen einen Container – können dafür aber auch "mehr".
Solange Unit-Tests reichen würde ich daher immer die nutzen, und das was du beschreibst klingt zumindest danach.




beta20 hat gesagt.:


> Mein Wildfly läuft ja derzeit sowieso.
> Aber wenn ich das richtig verstehe, dann wird speziell für das Testen nochmal eine eigene Instanz von Wildfly gestartet?
> 
> Falls dem nicht so ist, denke ich früher oder später benötige ich auch Tests mit laufendem Wildfly.


Nutzung eines laufenden oder Neustarten ist beides möglich, man kann das auch ohne Wildfly laufen lassen, ist nur mehr Aufwand, je nachdem was man braucht.


----------



## LimDul (11. Nov 2020)

beta20 hat gesagt.:


> Danke Dir.
> 
> Das würde mir auch erstmal reichen....
> Hast Du denn ein Sample Code? Was muss ich in der pom.xml einstellen usw. ? Welches jars benötige ich?


Leider nein. Ich kann leider schlecht internen Firmen-Code rauskopieren. Ist auch etwas mehr Code.


----------



## beta20 (11. Nov 2020)

mrBrown hat gesagt.:


> Unit-Tests testest "Units" in Isolation, ohne die durch die JakartaEE-Container Bereitgestellte Funktionalität.
> Integration Tests testen die Integration des ganzen, also wie das ganze im Container läuft.
> 
> Erstere sind schnell und einfach, letztere komplexer, langsamer und brauchen einen Container – können dafür aber auch "mehr".
> ...


Ja, mir würde das erstere auch reichen, sofern ich dann eben auch Datenbankzugriffe machen kann (hole Kunde mit ID=1 etc.). 
Nur leider scheitert es an der Config dazu...


----------



## LimDul (11. Nov 2020)

An vielen Stellen kommt man auch Mockito recht weit, in dem man Mocks in die Objekte reingibt.

Ist an der Stelle jetzt auch schwer ohne konkretes Beispiel zu sagen "So geht es". Es gibt viele Wege zum Ziel:

* Mock-Objekte mit z.B. Mockito
* Fake BeanProvider
* OpenEJB als Mini-Container
* Full-Blown Container mittels Arquillian

Grundsätzlich gilt, man sollte für Tests die niedrigst mögliche Variante wählen, weil sie dann entkoppelter und Schneller sind. Teilweise muss man dazu auch Klassen Refaktoren, dass sie testbar werden. Es ist z.B. meist nicht sinnvoll an allen Stellen "weil es einfach geht" @Inject dran zu schreiben. Denn dann sind die Objekte nachher wie ein Spinnennetz voneinander abhängig und nichts ist mehr entkoppelt und lose testbar.


----------



## Flown (11. Nov 2020)

LimDul hat gesagt.:


> Es ist z.B. meist nicht sinnvoll an allen Stellen "weil es einfach geht" @Inject dran zu schreiben. Denn dann sind die Objekte nachher wie ein Spinnennetz voneinander abhängig und nichts ist mehr entkoppelt und lose testbar.


Hier muss ich leider widersprechen. Gerade weil es einfach geht eine lose Kopplung zu haben. Genau diese @Injects kann man leicht mocken.


----------



## mrBrown (11. Nov 2020)

Flown hat gesagt.:


> Hier muss ich leider widersprechen. Gerade weil es einfach geht eine lose Kopplung zu haben. Genau diese @Injects kann man leicht mocken.


Ich glaube ihr meint beides das gleiche, @LimDul dürfte mit "an allen Stellen" hauptsächlich Felder meinen, aber Inject an Konstruktoren befürworten – und wie ich dich bisher erlebt hast dürftest du das ähnlich sehen.


----------



## LimDul (12. Nov 2020)

Wenn man sich zu kurz ausdrückt 

Inject macht es einfach auf Dinge zuzugreifen, weil ich mir keine Gedanken machen muss, wo es herkommt. Das ist erst mal gut und eine Lose Koppelung. Es kann aber auch dazu verleiten, dass man am Ende in einer Klasse 10+ Injects hat, weil "Ich brauch gerade dieses - einfach ein Feld definieren und @Inject dran, fertig. Man ist leicht verleitet anstelle sich genau zu überlegen, was brauche ich eigentlich in meiner Klasse an anderen Komponenten einfach alles zu nehmen.

Also Inject, insbesondere im Konstruktor, ist besser als die Objekte von Hand zu erzeugen und rumzureichen - ganz klar.
Aber noch besser ist, diese Sachen einfach gar nicht zu benötigen und Klassen zu haben, die mit 2 bis 3 anderen Klassen auskommen.


----------



## beta20 (12. Nov 2020)

Also ich habe mich nun mal an Mockito versucht. Eine Methode kann ich bereits aus dem EJB Container aufrufen (zB erwarteter Wert ist ein String), das funktioniert. Sobald ich aber eine Datenbankabfrage machen möchte, zB findCustomerById, dann bekomme ich nur einen NULL - Wert zurück, obwohl es den Kunden in der DB gibt.

Was mache ich hier falsch?
Ebenfalls die Frage, muss mein Wildfly dann immer laufen, wenn die die Tests ausführe oder wie wird sonst die DB Connection ausgeführt?

Hier mal mein Code:

```
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.MockitoRule;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {

    @Mock
    private CustomerService customerService;

    @Before
    public void setUp() {
        this.customerService = Mockito.mock(CustomerService.class);
    }
    
    @Test
    public void testQuery() throws CustomerNotFoundException {

        Customer customer = customerService.findCustomerById(Long.valueOf(1));
        assertNotNull(customer);
    }

}
```


Mein EJB Interface:

```
public interface CustomerService {
  Customer findCustomerById(Long id) throws CustomerNotFoundException;
}
```

Die EJB Implementierung

```
@Stateless

public class CustomerServiceBean implements CustomerService {   

    @PersistenceContext
      private EntityManager entityManager;

    public Customer findCustomerById(Long id) throws CustomerNotFoundException {

        Customer customer = new JpaQueryFunctions().findById(id, Customer.class, entityManager);
        return customer;
    }

}
```

Und hier noch die findById in meiner JpaQueryFunctions Util Klasse:

```
/**
     * Find
     */
    public <T> T findById(Long id, Class<T> entityClass, EntityManager entityManager) {

        T result = entityManager.find(entityClass, id);

        if (result == null)
            return null;

        return result;
    }
```


----------



## thecain (12. Nov 2020)

Im Test willst du ja auch nicht auf die Produktive DB zugreifen.

Und was willst du testen? Momentan heisst dein Test "MockitoTest" du mockst etwas und rufst nur das auf. Das bringt natürlich nichts... Wenn du den CustomerService testen willst, musst du eine Instanz vom CustomerService erstellen/erstellen lassen und den EntityManager mocken.

Dann noch allgemein:
- Warum ein Interface? Das ist seit etwas 5+ Jahre nicht mehr nötig bei EJB.
- Was bringt dir die findById Methode? Der EntityManager ist doch schon ein JpaQuerFunctionsUtil... zudem bringt dir die prüfung `if (result == null) return null` nichts


----------



## beta20 (12. Nov 2020)

thecain hat gesagt.:


> Im Test willst du ja auch nicht auf die Produktive DB zugreifen.
> 
> Und was willst du testen? Momentan heisst dein Test "MockitoTest" du mockst etwas und rufst nur das auf. Das bringt natürlich nichts... Wenn du den CustomerService testen willst, musst du eine Instanz vom CustomerService erstellen/erstellen lassen und den EntityManager mocken.


danke. 

Ich will testen, ob es den Kunden mit der ID =1 in meiner Datenbank gibt. Hierzu rufe ich die Methode "findCustomerId" auf.

Wie muss der Code dann entsprechend aussehen? 
Wäre das dann so:


```
@Mock
    private CustomerServiceBean customerServiceBean;

    @Mock
    private EntityManager entityManager;

    @Before
    public void setUp() {
       this.customerServiceBean = Mockito.mock(CustomerServiceBean.class);
         this.entityManager = Mockito.mock(entityManager.class);
        customerServiceBean.setEntityManager(entityManager);
    }
```



thecain hat gesagt.:


> Dann noch allgemein:
> - Warum ein Interface? Das ist seit etwas 5+ Jahre nicht mehr nötig bei EJB.
> - Was bringt dir die findById Methode? Der EntityManager ist doch schon ein JpaQuerFunctionsUtil... zudem bringt dir die prüfung `if (result == null) return null` nichts


1) War mir nicht bekannt, dass ich kein Interface mehr benötige.
2) Zu den letzten zwei Punkten, gebe ich dir Recht.


----------



## thecain (12. Nov 2020)

- Du darfst die Klasse die du testen willst nicht mocken.
- Dann brauchst du musst du auch noch die Methode mocken. so ähnlich:  `when(entityManager.find(MyClass.class, 1)).thenReturn(testCustomer)`


----------



## beta20 (12. Nov 2020)

Du meinst also so? Leider ist "testCustomer" dann immer noch NULL...


```
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {   

@Test
    public void testQuery() {
        
        Customer testCustomer = null;
        
        EntityManager entityManager = Mockito.mock(EntityManager.class);
        
        Mockito.when(entityManager.find(Customer.class, Long.valueOf(1))).thenReturn(testCustomer);
        System.out.println(testCustomer);
        
        assertNotNull(testCustomer);
    }
}
```


----------



## mrBrown (12. Nov 2020)

Mockito - Adding Behavior
					

Mockito - Adding Behavior,  Mockito adds a functionality to a mock object using the methods when(). Take a look at the following code snippet.




					www.tutorialspoint.com


----------



## beta20 (12. Nov 2020)

Danke für den Link. Aber ich bekomm es leider immer noch nicht hin zwecks Datenbankverbindung...

Der Test ist zwar erfolgreich, aber  System.out.println(customer) ist zB nicht customer = 1. Ebenfalls wenn ich nach ID = 2 suche sollte der Test doch fehlschlagen, weil es den Customer nicht in meiner Datenbank gibt. Prüfung wäre dann: (Assert._assertEquals_(*null*,customer);


```
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {

    @Mock
    CustomerService customerService;

    @InjectMocks
    Customer customer;

    @Test
    public void testQuery() throws CustomerNotFoundException {
                        
        Mockito.when(customerService.findCustomerById(Long.valueOf(1))).thenReturn(customer);
        System.out.println(customer);
    
        //test the add functionality
        Assert.assertEquals(customerService.findCustomerById(Long.valueOf(1)),customer);
        
         //verify the behavior
        Mockito.verify(customerService).findCustomerById(Long.valueOf(1));
    }
}
```


----------



## mrBrown (12. Nov 2020)

beta20 hat gesagt.:


> Danke für den Link. Aber ich bekomm es leider immer noch nicht hin zwecks Datenbankverbindung...


Der ganze Sinn von Mockito ist, dass du *keine Datenbank (-verbindung) nutzt.*

Hier:

```
public class MockitoTest {

CustomerService customerService;

@BeforeEach
void setUp() {
    // man erstellt ein "gefälschtes" CustomerService-Objekt
    customerService = mock(CustomerService.class); 
   
    // man gibt an, was die Fälschung bei welchen Methodenaufrufen machen soll:
   // Wenn die Methode customerService.findCustomerById mit dem Parameter 1 aufgerufen wird
    when(customerService.findCustomerById(Long.valueOf(1)))
        // dann gebe den Customer "Max Mustermann" zurück
        .thenReturn(new Customer(1L, "Max", "Mustermann"));
}

@Test
public void testQuery() throws CustomerNotFoundException {
    // der Aufruf von customerService.findCustomerById(1) ruft jetzt die Methode auf der Fälschung auf
    // Oben wurde angegeben, was dann passiert, und genau das passiert dann auch
    // es wird das oben erzeugt Customer-Objekt zurückgegeben
    Assert.assertEquals(customerService.findCustomerById(1).getFullName(), "Max Mustermann");
}
}
```


Allerdings ist der Test reichlich sinnlos. Mocken bringt dann etwas, wenn deine Klasse Dependencies hat, die du in dem Test nicht mit-testen willst. Sowas hast du in deinem Code aber überhaupt nicht (außer den EntityManager, aber fremde Typen mockt man nicht).

Mocken würde man den CustomerService, wenn eine andere Klasse den CustomerService nutzt, um den Customer zu suchen. Gibts sowas bei dir?


----------



## beta20 (12. Nov 2020)

Boah, vielen Dank!!
Jetzt habe ich es verstanden, dass diese Tests gar nicht gegen meine Datenbank laufen, diese Info war mir nicht klar.

Ja, habe ich.... Du meinst also zB sowas, dass ich bspw. die Anrede suche in einer Methode:


```
@Stateless
public CustomerServiceBean implements CustomerService {

    @EJB
    private SalutationService salutationService;
    
    public Customer findCustomer(Long id){
    
        // Anrede finden
        Salutation salutation = salutationService.findSalutationByName(String name);
        ....
    }
}
```

Das heißt ich müsste deinen Code so erweitern? Ist das richtig?


```
CustomerService customerService;

SaluationService saluationService;

@BeforeEach
void setUp() {
    // man erstellt ein "gefälschtes" CustomerService-Objekt
    customerService = mock(CustomerService.class);
     saluationService = mock(SaluationService.class);
....
}
```


----------



## thecain (12. Nov 2020)

Nein, die Komponente, die du testen willst, sollst du nicht mocken.


----------



## beta20 (12. Nov 2020)

thecain hat gesagt.:


> Nein, die Komponente, die du testen willst, sollst du nicht mocken.


aber das hat doch mrBrown auch gemacht? Siehe folgender Kommentar:



> // man erstellt ein "gefälschtes" CustomerService-Objekt
> customerService = mock(CustomerService.class);


Der Test läuft ja gegen CustomerService?


----------



## mrBrown (12. Nov 2020)

beta20 hat gesagt.:


> aber das hat doch mrBrown auch gemacht? Siehe folgender Kommentar:
> 
> 
> Der Test läuft ja gegen CustomerService?


Du übersiehst dabei diesen Kommentar:


mrBrown hat gesagt.:


> Allerdings ist der Test reichlich sinnlos.



Die Komponente unter Test darf man nicht verändern, man möchte sie ja testen.

Ganz dummes Beispiel: Crashtest bei Autos:
Beteiligt sind ein Auto, ein Fahrer, und irgendeine Strecke + Hindernisse.

Echte Fahrer möchte man ungern umbringen, also "mockt" man die und nutz einen Dummy, der nach außen "gleich" funktioniert.
Auf einer echten Straße möchte man das auch nicht machen, um nicht irgendwelche Passanten umzubringen, also "mockt" man die Straße auch und nimmt irgendeine Teststrecke mit Hindernissen.

Aber das Auto will man ja grad testen – dann das Auto zu mocken und durch einen Panzer zu ersetzen ist dumm. Man will ja genau dieses spezifische Auto testen.


----------



## beta20 (12. Nov 2020)

Ich habe jetzt doch nochmal ein Verständisproblem, konkret auf einen Testfall.
Als Ergebnis erwarte ich ein Datum


> contract.setLastBillingDate(LocalDateTime._of_(2020, 11, 1, 00, 00));


Ich weiß jetzt nicht, ob das ein Logikfehler meinerseits ist, da ich ein Contract als Parameter in die Funktion gebe und einen Contract wieder als Ergebnis erwarte (welches von der BillingInvoice referenziert wird)...

"createBillingInvoiceFromContractForRegularyFee" soll LastBillingDate auf die jetztige Uhrzeit setzen. Also in der Methode habe ich:


```
billingInvoice = new BillingInvoice();
billingInvoice.setContract(contract);
contract.setLastBillingDate(LocalDateTime.now());
return billingInvoice;
```

Also wenn ich dann im unteren Teil mit diesem Contract hereingehe:


> Contract contractTest = new Contract();


Dann soll in "createBillingInvoiceFromContractForRegularyFee" der Wert von LastBillingDate auf heute gesetzt werden.


```
@RunWith(MockitoJUnitRunner.class)

public class BillingInvoiceTest {

    @Mock
    BillingInvoiceProcessService billingInvoiceProcessService;

    @Before
    public void setUp() {
        billingInvoiceProcessService = Mockito.mock(BillingInvoiceProcessService.class);
    }

    @Test
    public void testQuery() {

        Contract contract = new Contract();
        contract.setLastBillingDate(LocalDateTime.of(2020, 11, 1, 00, 00));

        BillingInvoice billingInvoice = new BillingInvoice();
        billingInvoice.setContract(contract);

        Contract contractTest = new Contract();

        // man gibt an, was die Fälschung bei welchen Methodenaufrufen machen soll
        Mockito.when(billingInvoiceProcessService.createBillingInvoiceFromContractForRegularyFee(contract))
                .thenReturn(billingInvoice);


        Assert.assertEquals(billingInvoiceProcessService.createBillingInvoiceFromContractForRegularyFee(contractTest)
.getContract().getLastBillingDate(), LocalDateTime.now());

    }
```

Aber als Ergebnis habe ich:

```
java.lang.AssertionError: expected:<2020-11-01T00:00> but was:<2020-11-12T14:27:28.111546>
```


----------



## thecain (12. Nov 2020)

Du musst wirklich mal die Mockito Dokumentation anschauen. Das was du da machst macht keinen Sinn.

Und `LocalDateTime.now` nimmt `Clock` als parameter. Damit wird es auch testbar(er)


----------



## LimDul (12. Nov 2020)

Ein Beispiel (Spring Boot)


```
public class ZutatenServiceTest {

// Zugriff auf die DB wird gemockt
    private final ZutatenRepository zutatenRepository = Mockito.mock(ZutatenRepository.class);

// Erzeuge die zu testende Klasse
    private final ZutatenService service = new ZutatenService(zutatenRepository);

    private final List<Zutat> zutatenList = new ArrayList<>();

    private final TestRezeptModel testModel = new TestRezeptModel();

    @BeforeEach
    public void setUp() {
        // Mocke den Zugriff findAll der normalerweise auf die DB geht, dass er stattdessen die Liste liefert
        Mockito.when(zutatenRepository.findAll()).thenReturn(zutatenList);
    }



    @Test
    public void testFindeZutat_Name() {
        Zutat zutat1 = testModel.newZutat("Test");
        Zutat zutat2 = testModel.newZutat("Sonstiges");
        zutatenList.add(zutat1);
        zutatenList.add(zutat2);

        Optional<Zutat> zutat = service.findeZutat("test");

        assertThat(zutat.isPresent(), is(true));
        assertThat(zutat.get(), is(zutat1));
    }
```

Das heißt, ich teste hier das die Methode "findeZutat" das richtige liefert (sofern die Datenbank bei findAll das erwartete liefert)


----------



## beta20 (12. Nov 2020)

Also ich glaube ich habe ein grundlegendes Verständnisproblem zu diesem Framework - in der Doku drehe ich mich nur noch im Kreis, was mich mittlerweile frustriert.
Es scheint mir so, dass meine Methode überhaupt gar nicht aufgerufen wird?

Im Grunde möchte ich eigentlich nur eine Methode (im EJB Container) aufrufen mit Parameter befüllen und dann das Ergebnis mit einem erwarteten Ergebnis vergleichen...

Die letzte Codezeile ist mein Vergleich. Der Test gibt immer wahr zurück, weil ich oben deklariere, dass 1 rauskommt und unten soll auch 1 rauskommen. Aber die Methode wird gar nicht aufgerufen, denn sonst würde ja was anderes rauskommen (angenommen die Implementierung wäre einfach 1+1).
Wie vergleiche ich aber den wirklichen Output der Methode?


```
@Mock
    MiscBillingTimeService miscBillingTimeService;  

    @Test
    public void testQuery1() {

        LocalDateTime startDate = LocalDateTime.of(2020, 9, 10, 0, 0);
        LocalDateTime usedEndDate = LocalDateTime.of(2020, 10, 1, 0, 0);
        String periodUnit = DateTimeType.MONTH.toString();
        int periodValue = 1;
        double feeNetValuePerUnit = 25;

        Mockito.when(miscBillingTimeService.calculateUsedUsage(startDate, usedEndDate, periodUnit, periodValue,
                feeNetValuePerUnit)).thenReturn(1.0);      

Assert.assertEquals(miscBillingTimeService.calculateUsedUsage(startDate, usedEndDate, periodUnit, periodValue,
                feeNetValuePerUnit), 1.0, 0);
    }
```


----------



## mrBrown (12. Nov 2020)

beta20 hat gesagt.:


> "createBillingInvoiceFromContractForRegularyFee" soll LastBillingDate auf die jetztige Uhrzeit setzen


Tests mit Uhrzeiten sind noch mal eine anderen Hausnummer, die normale Erzeugung der Daten kannst du so nie nutzen, sondern musst immer explizit eine Clock übergeben, damit es testbar wird.



beta20 hat gesagt.:


> "createBillingInvoiceFromContractForRegularyFee" soll LastBillingDate auf die jetztige Uhrzeit setzen. Also in der Methode habe ich:


Welche Klasse soll denn getestet werden? Deinen Erklärungen nach der BillingInvoiceProcessService, aber genau den Mockst du ja – und deshalb ist der Test auch wieder Unsinn (zusätzlich dazu, dass man mit Uhrzeiten so nicht testen kann).

Was du da ganz primitiv ausgedrückt machst ist, Auf einen Zettel "A" schreiben, und den in eine Box legen.
Und dann später in die Box gucken und prüfen, ob da der Zettel mit einem "A" drauf drin liegt – was, wenig überraschend, stimmt.




BTW: deine Assertions sind falsch herum, den erwarteten Wert musst du als erstes übergeben.:


beta20 hat gesagt.:


> Aber als Ergebnis habe ich:
> 
> ```
> java.lang.AssertionError: expected:<2020-11-01T00:00> but was:<2020-11-12T14:27:28.111546>
> ```


----------



## mrBrown (12. Nov 2020)

beta20 hat gesagt.:


> Wie vergleiche ich aber den wirklichen Output der Methode?


Indem du die Methode *nicht* mockst.

Man nutzt Mocks, wenn deine Methode Abhängigkeiten hat, die du nicht mit-testen willst.
Du brauchst also mindestens zwei Klassen, wobei eine davon die andere nutzt (also davon abhängt), z.B. zwei Services.

Wenn du keinerlei Abhängigkeiten hast, brauchst du auch keine Mocks. 


Ich weiß, das sag ich jetzt zum etwas siebenundzwanzigsten Mal, aber: Du solltest wirklich die Grundlagen mit etwas kleinerem als dem Projekt üben. Ein riesiges Enterprise-Projekt entwickeln, ohne die Grundlagen zu beherrschen, ist meistens zum Scheitern verurteilt...


----------



## beta20 (12. Nov 2020)

Habe es nun so gemacht, was auch nun funktioniert und die Methode aufruft...  Aber ist das so auch korrekt?


```
@Mock
    MiscBillingTimeService miscBillingTimeService;

  
    @Test
    public void testQuery1() {

        MiscBillingTimeServiceBean miscBillingTimeServiceBean = new MiscBillingTimeServiceBean();
      
        LocalDateTime startDate = LocalDateTime.of(2020, 9, 10, 0, 0);
        LocalDateTime usedEndDate = LocalDateTime.of(2020, 10, 1, 0, 0);
        String periodUnit = DateTimeType.MONTH.toString();
        int periodValue = 1;
        double feeNetValuePerUnit = 25;

        Mockito.when(miscBillingTimeService.calculateUsedUsage(startDate, usedEndDate, periodUnit, periodValue,
                feeNetValuePerUnit)).thenReturn(1.0);

      
        Assert.assertEquals(1.0, miscBillingTimeServiceBean.calculateUsedUsage(startDate, usedEndDate, periodUnit, periodValue,
                feeNetValuePerUnit), 0);
    }
```


----------



## mrBrown (12. Nov 2020)

beta20 hat gesagt.:


> Habe es nun so gemacht, was auch nun funktioniert und die Methode aufruft... Ist das so korrekt?


Naja, abgesehen davon, dass miscBillingTimeService gar nicht benutzt wird und daher die Nutzung von Mockito völlig überflüssig ist...


----------



## beta20 (12. Nov 2020)

mrBrown hat gesagt.:


> Naja, abgesehen davon, dass miscBillingTimeService gar nicht benutzt wird und damit die Nutzung von Mockito völlig überflüssig ist...


doch hier?

Mockito.when(*miscBillingTimeService*.calculateUsedUsage(startDate, usedEndDate, periodUnit, periodValue,
                feeNetValuePerUnit)).thenReturn(1.0);

Würde ich ebenfalls mit *miscBillingTimeServiceBean *das machen, bekäme ich diese Exception

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

----------
OK, es scheint so, dass ich ein ganz anderes Verständnis gehabt habe bzgl. EJB zu testen. Ich dachte ich benötige hier Arquillian oder Mockito.
Aber ich sehe gerade, dass auch dieser Test alleine funktioniert:


```
Assert.assertEquals(1.0, miscBillingTimeServiceBean.calculateUsedUsage(startDate, usedEndDate, periodUnit, periodValue,
                feeNetValuePerUnit), 0);
```


----------



## mrBrown (12. Nov 2020)

beta20 hat gesagt.:


> doch hier?
> 
> Mockito.when(*miscBillingTimeService*.calculateUsedUsage(startDate, usedEndDate, periodUnit, periodValue,
> feeNetValuePerUnit)).thenReturn(1.0);


Mit dem Teil sagst du nur "wenn irgendwann später einmal *miscBillingTimeService*.calculateUsedUsage aufgerufen wird, dann mache dies"




beta20 hat gesagt.:


> OK, es scheint so, dass ich ein ganz anderes Verständnis gehabt habe bzgl. EJB zu testen. Ich dachte ich benötige hier Arquillian oder Mockito.


Arquillian brauchst du, wenn du einen Container zum Ausführen der Tests brauchst.
Mockito dann, wenn du Abhängigkeiten mocken musst – ob du das musst, hängt ganz vom Test ab. Bei größeren Anwendungen braucht man meist irgendeine Möglichkeit, Abhängigkeiten zu mocken, weil meist sehr vieles irgend eine Abhängigkeit hat, das hängt aber immer von den konkret zu testenden Klassen ab.

Wenn du Klassen ohne Abhängigkeiten hast ist das perfekt, besser zu testen ist quasi nichts.


----------



## beta20 (12. Nov 2020)

Ok, danke.... Dazu noch eine Frage:

Wenn ich nun in der folgenden Methode von (*miscBillingTimeServiceBean.calculateUsedUsage*) eine Methode von einem anderen Interface (EJB) aufrufe:

also bspw.
void *calculateUsedUsage() {
....
contractService*.findContract(1);
}
wie,wo, was muss ich dann *contractService* deklarieren, denn derzeit verweißt diese ja auf NULL beim Aufruf von *miscBillingTimeServiceBean.calculateUsedUsage*....

Ich dachte nun an:

@InjectMocks
ContractService contractService

Aber das scheint nicht zu funktionieren


----------



## mrBrown (12. Nov 2020)

Verzichte erstmal auf die Annotationen und mach's per Hand:


```
class MiscBillingTimeServiceBeanTest {
    
private MiscBillingTimeServiceBean miscBillingTimeServiceBean;

@BeforeEach
void setUp() {
    Contract contract = ....

    ContractService contractService = Mockito.mock(ContractService.class);
    Mockito.when(contractService.findContract(1)).thenReturn(contract)

    miscBillingTimeServiceBean = new MiscBillingTimeServiceBean(contractService);//ich hoffe mal du nutzt Konstruktor-Injection
}

}
```


----------



## beta20 (12. Nov 2020)

mrBrown hat gesagt.:


> miscBillingTimeServiceBean = new MiscBillingTimeServiceBean(contractService);//ich hoffe mal du nutzt Konstruktor-Injection
> [/code]


Das funktioniert bei mir so leider nicht...
Ich Injecte die Beans in MiscBillingTimeServiceBean so:

 @EJB
*private* ContractService contractService;

Welche Alternativen gibt es?


----------



## thecain (12. Nov 2020)

Constructor Injection zu verwenden wäre die einfachste


----------



## mrBrown (12. Nov 2020)

beta20 hat gesagt.:


> Welche Alternativen gibt es?


Konstruktor-Injection statt Field-Injektion zu nutzen 🤷‍♂️


----------

