# Probleme mit Cactus



## dumm_aber_schlau (7. Apr 2009)

Hallo *.*,

ich bin derzeit dabei mich ein wenig in Cactus einzuarbeiten, stoße dabei aber auf ein Problem. Ich möchte ein Servlet testen und habe für dessen zwei Methoden Tests geschrieben. Der erste Test, "testGetDokumentennummer()" funktioniert einwandfrei.

Beim zweiten Test habe ich das Problem das die beginXXX()- und endXXX()-Methode zweimal aufgerufen wird. Beim beginXXX() stört mich das nichtmal, aber beim endXXX() ist beim zweiten mal der Content den ich vorher im geschrieben habe null.

Um das ganze zu verdeutlichen poste ich hier erstmal meine beiden Klassen, und anschließend die Ausgabe in der Eclipse-Console. Die Ausgaben auf der Console wurden nur zu Debugging-Zwecken eingebaut.

Die Testklasse:

```
import java.io.IOException;

import javax.servlet.ServletException;

import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;
import org.apache.cactus.WebResponse;

public class TestGetVersionServlet extends ServletTestCase {
	public static final String PARAMETER_DOKNR = "doknr";
	public static final String DOKNR_WRONG = "ABC";
	public static final String DOKNR_CORRECT = "DEF";

	public TestGetVersionServlet(String theName) {
		super(theName);
	}

	public void beginGetDokumentennummer(WebRequest theRequest)
	{
		theRequest.addParameter(PARAMETER_DOKNR, DOKNR_CORRECT);
	}

	public void testGetDokumentennummer() {
		GetVersionServlet servlet = new GetVersionServlet();
		
		// Methode aufrufen!
		String str = servlet.getDokumentennummer(request);

		// Test
		assertEquals("dokumentennummer", DOKNR_CORRECT, str);
		assertFalse(str.equalsIgnoreCase("DCM4711"));
		assertFalse(str == null);
	}

	public void beginPostVersionWrong(WebRequest theRequest)
	{
		theRequest.addParameter(PARAMETER_DOKNR, DOKNR_WRONG);
		System.out.println("beginPostVersionWrong()");
	}

	public void testPostVersionWrong() {
		System.out.println("testPostVersionWrong()");
		GetVersionServlet servlet = new GetVersionServlet();
		try {
			servlet.doGet(request, response);
		} catch (ServletException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void endPostVersionWrong(WebResponse theResponse)
	{
		// Get the returned content as a string
		String content = theResponse.getText();

		System.out.println("endPostVersionWrong.content: " + content);

		// Do some asserts
		assertTrue("content == null", content != null);
		assertTrue(DOKNR_WRONG + " not found", content.indexOf(DOKNR_WRONG) != -1);
	}
}
```

Das Servlet:

```
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GetVersionServlet extends HttpServlet
{
	public String getDokumentennummer(HttpServletRequest request) {
		String dokNummer = request.getParameter("doknr");
		return dokNummer;
	}
	
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		response.setContentType("text/html");
		
		PrintWriter  out = response.getWriter();
		out.println("<html><head></head><body>Dokumentennummer: " + getDokumentennummer(request) + "</body></html>");
		out.close();
	}
	
}
```

Die Ausgaben in der Console (Tomcat):

```
[...]
07.04.2009 14:17:36 org.apache.coyote.http11.Http11BaseProtocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
07.04.2009 14:17:36 org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 436 ms
07.04.2009 14:17:36 org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
07.04.2009 14:17:36 org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/5.5.27
07.04.2009 14:17:36 org.apache.catalina.core.StandardHost start
INFO: XML validation disabled
07.04.2009 14:17:36 org.apache.coyote.http11.Http11BaseProtocol start
INFO: Starting Coyote HTTP/1.1 on http-8080
07.04.2009 14:17:36 org.apache.jk.common.ChannelSocket init
INFO: JK: ajp13 listening on /0.0.0.0:8009
07.04.2009 14:17:36 org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=0/31  config=null
07.04.2009 14:17:37 org.apache.catalina.storeconfig.StoreLoader load
INFO: Find registry server-registry.xml at classpath resource
07.04.2009 14:17:37 org.apache.catalina.startup.Catalina start
INFO: Server startup in 546 ms
beginPostVersionWrong()
beginPostVersionWrong()
testPostVersionWrong()
endPostVersionWrong.content: <html><head></head><body>Dokumentennummer: DCM0815</body></html>

endPostVersionWrong.content:
```

Wenn mir jetzt jemand erklären kann warum die begin()- und end()-Methode jeweils zweimal aufgerufen wird und / oder was ich falsch mache, wäre ich verdammt dankbar!


----------



## maki (7. Apr 2009)

Ehrlich gesagt sehe ich nicht warum diese Methoden überhaupt aufgerufen werden sollten... gibt es da etwas das wir noch nicht wissen?

Nachtrag: Achso, ganz klar, diese Methoden werden von Cactus aufgerufen, die beginXX Methoden vor *jedem* Testfall, die endXXX Methoden nach *jedem* Testfall (= Testmethode).

Alles klar?


----------



## dumm_aber_schlau (7. Apr 2009)

Hallo maki,




maki hat gesagt.:


> Ehrlich gesagt sehe ich nicht warum diese Methoden überhaupt aufgerufen werden sollten... gibt es da etwas das wir noch nicht wissen?
> 
> Nachtrag: Achso, ganz klar, diese Methoden werden von Cactus aufgerufen, die beginXX Methoden vor *jedem* Testfall, die endXXX Methoden nach *jedem* Testfall (= Testmethode).
> 
> Alles klar?



mir ist (hoffentlich) klar wann die Methoden von Cactus aufgerufen werden.  In den Aufrufen setze ich entsprechende Request-Parameter für das Servlet. Diese Request-Parameter werden im Servlet ausgewertet.

In der eigentlichen Test-Methode kann ich den Request-Parameter leider nicht setzen. Rein technisch gesehen scheinbar schon, denn es gibt auch dort die Möglichkeit request.setAttribute() aufzurufen, die Werte die ich dort eintrage kommen aber nicht bei meinem Servlet an.

Nachtrag: 
Die beginXXX()- und die endXXX()-Methode wird für jeden Testfall exakt einmal(!) aufgerufen, und nicht wie bei mir zweimal!

Gruß
DAS


----------



## maki (7. Apr 2009)

> In der eigentlichen Test-Methode kann ich den Request-Parameter leider nicht setzen. Rein technisch gesehen scheinbar schon, denn es gibt auch dort die Möglichkeit request.setAttribute() aufzurufen, die Werte die ich dort eintrage kommen aber nicht bei meinem Servlet an.


Denke (habe selbst noch keine Servlet Tests mit Cactus geschrieben, nur EJB Tests) das Problem wird sein, dass diese Methoden auf der Serverseite ausgeführt werden.
Da kann man nicht einfach eine neu Parameter an einen bereits geparsten Request hängen, bei einem GET Request würde sich zB. die URL ändern, bei einem POST der Body... Cactus bietet ja eigene Methoden dafür an die so etwas können sollten.



> Nachtrag:
> Die beginXXX()- und die endXXX()-Methode wird für jeden Testfall exakt einmal(!) aufgerufen, und nicht wie bei mir zweimal!


Naja, du hast 2 Testfälle (2 testXXX Methoden), warum die Reihenfolge nicht stimmt verstehe ich aber auch nicht...


----------



## dumm_aber_schlau (7. Apr 2009)

Hallo maki! 



maki hat gesagt.:


> Denke (habe selbst noch keine Servlet Tests mit Cactus geschrieben, nur EJB Tests) das Problem wird sein, dass diese Methoden auf der Serverseite ausgeführt werden.
> Da kann man nicht einfach eine neu Parameter an einen bereits geparsten Request hängen, bei einem GET Request würde sich zB. die URL ändern, bei einem POST der Body... Cactus bietet ja eigene Methoden dafür an die so etwas können sollten.


Genau aus diesem Grund benutze ich die Methoden ja auch. Mal ein entsprechendes Zitat von der Cactus Homepage:


> Step 6 (optional): beginXXX() methods
> For each XXX test case, you can define a corresponding beginXXX() method (optional). You will use it to initialize HTTP related parameters (HTTP parameters, cookies, HTTP headers, URL to simulate, ...).






> Naja, du hast 2 Testfälle (2 testXXX Methoden), warum die Reihenfolge nicht stimmt verstehe ich aber auch nicht...


Die Reihenfolge ist unerheblich und auch bei JUnit nicht vorhersehbar. 

Aber trotzdem stimmt Deine andere Annahme nicht ganz. Ich habe zwar zwei Testfälle, aber zu jedem Testfall nur maximal EINE begin()- und eine end()-Methode. Ergo habe ich einmal Testfall XYZ mit einer begin()-Methode und einmal Testfall ABC mit begin()- und end()-Methode. Das erklärt noch immer nicht meiner Meinung nach, warum beginABC() und endABC() zweimal aufgerufen werden... :-(

Gruß und vielen Dank für Deine Mühen,
DAS


----------



## maki (7. Apr 2009)

> Die Reihenfolge ist unerheblich und auch bei JUnit nicht vorhersehbar.


Mit Reihenfolge meinte ich, dass erst die beginXXX Methoden zweimal aufgerufen werden, nach dem test die endXXX Methoden 2 mal, sollte nach meinem Verständnisso laufen: 1. beginXXX 2. testXXX 3. endXXX

Denke wir meinen beide dasselbe, reden aber nur aneinander vorbei.

Weiss nciht ob ich dir wirklich weiterhelfen kann, aber einen Versuch ist es Wert.

Wie startest du deine tests eigentlich?


----------



## dumm_aber_schlau (7. Apr 2009)

Hallo maki! 



maki hat gesagt.:


> Mit Reihenfolge meinte ich, dass erst die beginXXX Methoden zweimal aufgerufen werden, nach dem test die endXXX Methoden 2 mal, sollte nach meinem Verständnisso laufen: 1. beginXXX 2. testXXX 3. endXXX
> 
> Denke wir meinen beide dasselbe, reden aber nur aneinander vorbei.
> 
> ...



Ja, Du hast Recht, wir könnten das gleiche meinen. 

Eigentlich sollen die Testfälle unabhänging von einander laufen. Also wäre der eigentliche Ablauf:
1. beginABC()
2. testABC()
3. beginXYZ()
4. testXYZ()
5. endXYZ()

oder

1. beginXYZ()
2. testXYZ()
3. endXYZ()
4. beginABC()
5. testABC()

So, aber nicht anders sollte der Ablauf sein.

Ich starte meine Tests über eine Testsuite:

```
import org.apache.cactus.ServletTestSuite;

import junit.framework.Test;
import junit.framework.TestCase;

public class MyTestSuite extends TestCase 
{
    public static Test suite()
    {
        ServletTestSuite suite = new ServletTestSuite();
        suite.addTestSuite(TestGetVersionServlet.class);
        return suite;
    }
}
```

Im Browser rufe ich dann noch die folgende Adresse auf:

```
http://localhost:8080/test/ServletTestRunner?suite=MyTestSuite&xsl=cactus-report.xsl
```

Da die anderen Tests jedoch funktionieren, gehe ich nicht davon aus das es am Aufruf liegt, oder hättest Du da eine Idee?

Gruß
DAS

Nachtrag:
Interessanterweise ruft er den anderen Testfall auch zweimal auf, also die begin()-Methode. Dort ist es nur kein Problem, weil die Methode direkt etwas zurück gibt und nicht in das Response-Objekt schreibt scheinbar...


```
beginGetDokumentennummer()
beginGetDokumentennummer()
testGetDokumentennummer()
beginPostVersionWrong()
beginPostVersionWrong()
testPostVersionWrong()
endPostVersionWrong.content: <html><head></head><body>Dokumentennummer: DCM0815</body></html>

endPostVersionWrong.content:
```


----------



## maki (7. Apr 2009)

Laut diesem Post RE: beginXXX and endXXX called twice sollte man ein eigenes TestSetup erstellen, ob das wirklich hilft weiss ich allerdings nicht.


----------



## dumm_aber_schlau (7. Apr 2009)

Hallo maki 



maki hat gesagt.:


> Laut diesem Post RE: beginXXX and endXXX called twice sollte man ein eigenes TestSetup erstellen, ob das wirklich hilft weiss ich allerdings nicht.



Danke für den Link, ich schaue mir das gleich mal an. Aber ich habe gerade das Problem eingrenzen können. Ich habe nämlich mal Spaßeshalber (haha!) dem Browser statt meiner Testsuite nur meine Testklasse als Parameter übergeben:

```
http://localhost:8080/test/ServletTestRunner?suite=TestGetVersionServlet&xsl=cactus-report.xsl
```

Und was sehen meine Augen?

```
[...]
INFO: Server startup in 563 ms
beginGetDokumentennummer()
testGetDokumentennummer()
beginPostVersionWrong()
testPostVersionWrong()
endPostVersionWrong.content: <html><head></head><body>Dokumentennummer: DCM0815</body></html>
```

Er macht es exakt wie er es sollte... Also bin ich zu blöd eine Testsuite zu erstellen... Interessant... 

Gruß
DAS


----------



## dumm_aber_schlau (7. Apr 2009)

maki hat gesagt.:


> Laut diesem Post RE: beginXXX and endXXX called twice sollte man ein eigenes TestSetup erstellen, ob das wirklich hilft weiss ich allerdings nicht.



Link angesehen. Vielen Dank dafür, jetzt gucke ich erstmal wie ich ein TestSetup erstelle und was das genau ist. Daaaanke! 

Gruß
DAS


----------

