Table of Contents
In the previous chapter, a test case was built from a recording, and in both setups, i.e. with a simulated and a real setup, the test was successful. But this test case does only check the status codes of the returned pages, and the communication of the participants in the test case is defined statically and does not adapt to the real communication. E.g. if a server reply contains a session ID, that should be further used by the client, the client does not use this ID for the future communication but it uses the session ID that was transmitted at the creation of the test case.
For advanced applications this behavior is not sufficient. This chapter gives an idea how test cases, that have been made with a recording, can be modified so that the dynamic data of the communication is used, and the test case becomes more useful.
In this chapter the test case that was built in the previous chapter will be used.
Let's change the test case and look what's happening to visualize that the test case we have until now is not very useful. The sent password will be altered, and the expected result will be a failure, because the search site of the hotel booking application is not reached.
To change the sent password, open the template
0002-Request in the folder
generated/bookingExampleLoginTest and edit it. The file might appear
empty in the design view because the file does not contain XML code, but the content of the
file becomes visible in the Source tab of the XML editor.
The sent request is hard to read, but the right place of the password can be
found when searching the string
password= in the template. Change the value of the string, save and rerun
the test case.
The test case does not fail, as expected. To explain this behavior the third request/response pair of the generated test case can be examined:
It can be seen that the only assertion in the
0003-Check event asserts that the status of the returned site is OK
(200). And this is the case, even if the login page and not the hotel search page is
returned.
To ensure that the main page of the application is displayed, an additional
assertion must be added that tests if the correct page is sent. An easy way to test this
could be checking if the text "Search Hotels" can be found in the returned web site. This
must be done at the event
0003-Check. The returned web site is a HTML site, so for further
processing it is necessary to make the returned site XML compliant. This can be done with
help of the predefined filter
HTMLCompletion. Add this filter by dragging the
button to the event
0003-Check, selecting the filter
HTMLCompletion and pressing the button.
![]() | HTMLCompletion |
|---|---|
|
HTML as opposed to XHTML is not well-formed XML and therefore not a valid XML document. This filter manipulates the document it receives in a way that the resulting document will be well-formed XML (e.g. by completion of non-closed element tags). |
Now it can be asserted that the returned page contains the string "Search Hotels".
In this example it is sufficient to test if the returned page contains a node that
contains the text "Search Hotels". This can be asserted if the XPath expression
//*[contains(.,"Search Hotels")] has a result.
Now add the assertion:
Drag the button to the event
0003-Check
Name the assertion
isSearchHotelPage
Choose the
exits operator
Choose
String for the data type
Press in the left column of the dialog
Select the
Path argument and then set it to
//*[contains(.,"Search Hotels")] by pressing and specifying the expression.
Press
The assertion should now look like in Figure 13.5, “ The assertion in the event ”
Now rerun the test case. The result is still a successful test run, although the password for the login is still set wrong.
To explain this behavior the test case must be examined further. Take a look at the
event that performs the sending of the login data, i.e.
0002-Request. This request does not request the main page of the
application, that contains the search form, as expected. It sends the login data to the
login page itself. The server replies with a redirect to the main page, which is the
search page.
As an experiment, open a browser window and try to load the page to which the server
redirects. The full address of this page can be found in the
redirect parameter of the protocol of the redirecting event, i.e.
0002-Response. Copy the full URL (with the query parameter) to the
location bar of the browser. The browser will display the main page, but there is no user
logged in.
This behavior is related with the query parameter
conversationId. This ID is used by JBoss Seam to determine which page is
sent back.
Examining the request following the redirect makes clear why the server responds
always in the same way: The conversation header parameter of the request's protocol is
set to a fixed value. To treat the server's redirects in the right way, the
conversationId parameter must be saved from the redirect and than used
in the next request.
The saving of the
conversationId must be done multiple times in this test case. To avoid
duplicate code the saving of the
conversationId
will be done in a module, that can be inserted at the appropriate places
at the test case:
Create a module called
handleRedirect
Add the actor
Client to the module, using the Actor
setup dialog in the context menu
Drag the
button to the lifeline of the Client actor, and name the event
handleRedirect
First the value of the request parameter must be saved. The complete URL of the
redirect is stored in the context
_peta/transport/header/location. The
conversationId parameter will be fetched from the URL with the
predefined plug-in
ExtractQueryParameterFromURL. Do this by performing the following
steps:
Add a new context to the event
handleRedirect by dragging the
button to the event
Name the new context variable
conversation
Add the plug-in
ExtractQueryParameterFromURL
Set the
URL argument to a reference to the context variable
_peta/transport/header/location
Set the
key argument to
conversationId
Although the path that is returned by the redirect never changes, it should be treated correctly. Do this with the following steps:
Add an other context, and name it
path
To extract the path from an URL, the plug-in
ExtractPathFromURL is predefined. Add this plug-in.
Set the
URL argument to a reference to the context variable
_peta/transport/header/location
The module created in the last step can be now used in the test case: Insert the module
after each redirect, i.e. after the events
0002-Check and
0004-Check.
Now, all parameters that are necessary to perform the redirect are known, so the requests following the redirects can be modified in a way that they request the correct page:
Expand event
0003-Request
Double click on the protocol to edit its properties
Change the constant value of the argument
query/conversationId to a reference to the context variable
conversation
Change the constant value of the argument
path to a reference to the context variable
path
As there is an other redirect in event
0004-Response, repeat the last steps for event
0005-Response.
Now, the test case is ready to be run. Click the button in the toolbar to rerun the test case. As expected,
the test case fails. Now change the password in the template
0002-Request back to the correct value and run the test case
again. It will succeed.
The test case works now correctly, but there is still one flaw: The session ID, that
is transmitted in the requests, e.g. in
0002-Request is not changed dynamically but is set to a fixed value.
Although in this case the test case works correct, this may lead to problems.
Event
0001-Request requests the login form of the web application. This is
sent back and in the next event the client sends a request with method
post and the data that was filled in the form in the body of the
message.
Now, let's examine the form that is sent by the server. Open the template
0001-Response. It contains the HTML code of the login that is
displayed by the browser. The page contains a form. All the data that was entered in the
form will be committed to the server in the next request of the client. The way how the
data is committed is defined with the attributes
method and
action of the form element. The
method attribute defines, as it name says, the method that is used to
commit the data, i.e.
get or
post. In this example the
method is set to
post, i.e. the data will be sent in the body of the request. The
action attribute defines the URI where to send the data. The URI may
just consist of a path. In this case the server to whom the last request was sent should
be addressed in the next request. If the path is relative, the path relative to the last
requested path should be used. In this example the action attribute is set to
/home.seam;jsessionid=A98F7477D17A1CB712D1A73B9ABE68ED , i.e.
URI /home.seam is addressed on the server that was addressed in the preceding event.
The stuff behind the ; is called the parameter.
The parameter changes every time the form is requested. This can be seen in the
log, when the test case is run multiple times and the log level in the launch
configuration is set to
debug. Consult Chapter 6, Create a launch configuration for
details on how the log level is set in a launch configuration. But the generated test
case always sends requests with the jsessionid that was correct at the recording
time. This can be seen in the log file and in the template
0002-Request.
In the following it will be shown how the jsessionid parameter can be extracted from the form and how it can be sent in the body of the next request to the server.
As a first step, the
action attribute of the
form element will be extracted from the last sent document. Then the
value of the
jsessionid parameter will be extracted from the complete value of
the
action string. Here we don't have a predefined plug-in to do that, so
the value has to be extracted by hand. If this has to be done often, it might be taken to
account to build a new plug-in that accomplishes this task, because in this way it is
easier to reuse but more laborious. So, for this example the built-in Eval-plug-in
will be used. This plug-in can be utilized to evaluate simple
expressions.
In the last step the template
0002-Request will be replaced by a filter that creates the request
body dynamically.
So let's go to work: First extract the
action attribute from the last received document. The action
attribute is part of the
form element, which is part of the
body of a file with root element
html. So the
action attribute can be easy found with the XPath expression
/html/body/form/@action. Create a context variable called
action that stores the variable:
To make the test case more clear, the extraction of the jsessionid
string will be put in a single event. Therefore drag the button to
the lifeline of the client, below the event 0001-Check and name the event getSessionID.
Add a context by dragging the
button to the event we have just created and name it
action.
We want to access the last received document, so press the button.
Select the
path argument, press and enter
/html/body/form/@action to assign the value of the
action attribute to the context variable
action.
Now, the value of the
action attribute of the form can be accessed through the
action context variable.
The following step will be more complicated. First the position of the string
jsessionid= must be found. This is done by evaluating the function
lastIndexOf with the Eval-plug-in.
![]() | The Eval-plug-in |
|---|---|
|
The Eval-plug-in is a plug-in that is designed to evaluate simple expressions "on the fly" if writing a new plug-in is too costly. It provides many arithmetical, string and boolean operators, which are mostly similar to their equal named java counterparts. Consult the PETA-Core Reference Manual for a complete list of the available functions. A defined variable can be referenced in the expression to evaluate with the
syntax
The variables are defined with the help of keyed parameters. These parameters
can be, depending on their type,
The Eval-plug-in can transform the returned value, depending on its data type
to a correct string. This is specified by using the argument
For further information about the Eval-plug-in, consult the PETA-Core Reference Manual. |
For the position of the
jsessionid value in the action string, a new context variable will
be defined:
Drag the button to the event
getSessionID, beneath the definition of the
action context variable. And name the new context
indexOfSessionID.
Add the Eval-plug-in by pressing button.
Enter an expression that computes the position of the
jsessionid string, e.g.
lastIndexOf(#{action},'jsessionid=',length(#{action})) .
The
lastIndexOf(String s, String str, int fromIndex) function
returns the position of the string
str in the string
s searching from the position
fromIndex. The
length function returns the length of a string.
Now the variable action must be passed to the Eval-plug-in. To do this
Select the Eval-plug-in
Press Add Argument
Select
string and press
Enter
action as key and press the
button.
Select the string argument and press
Enter
action as key or select the
action context variable in the assisting dialog that
is shown if has been
pressed.
Fianlly the return type must be specified. To do this
Select the Eval-plug-in and press
Select the returnType argument and press
Select the returnType's value, press and enter
number for the value. This lets the plug-in return the
result in a sensible format for numbers.
We have now the position of the
jsessionid parameter in the URI of the action attribute of the
received form. With the help of this, the value of the
jsessionid parameter can be taken from the URI. This will be done
again with the Eval-plug-in.
The value will be stored in a new context variable named
jsessionID.
To add the context variable drag the
button to the event
getSessionID below the context variable definition
indexOfSessionID.
Name the context variable sessionID
Add the
Eval-plug-in by pressing and selecting the Eval-plug-in
Select the argument
expression and press . Enter
substring(#{action}, #{indexOfSessionID},
length(#{action})) as value and press
. The
substring(String s, int begin, int end) does the same as its
Java counterpart: It returns a substring of
s that begins with the index
begin and ends with the index
end.
The expression contains two variables, i.e,
#{action} and
#{indexOfSessionID}. These variables must be assigned by a
parameter.
First assign the context variable
action to the
#{action} variable in the Eval expression. This is
done with the same steps as above:
Select the
Eval-plug-in press select
string and press then
enter
action as key and press .
Select the new argument, and press . Enter
action as key and press
.
Next the context variable
indexOfSessionID will be assigned to the second
variable
#{indexOfSessionID}
#{indexOfSessionID}: This will be done as for the
variable
#{action} with the difference that the
indexOfSessionID context variable should be
interpreted as a number and not as a string. So the argument that
passes the context variable to the
Eval expression must be in this case a number.
Select the
Eval plug-in, press select
number and press then
enter
indexOfSessionID and press
.
Next select the argument
indexOfSessionID and press , enter indexOfSessionID and
press .
Figure 13.21. Definition of the sessionID context variable with assignment of the indexOfSessionID variable
![]() |
The expected value of the
substring function is a string, so the
returnType argument must be set to
string. To do this, select the
Eval-plug in, press select the
returnType argument. Press
and select the
returnType argument, press and enter
string.
Now the
sessionid parameter will be stored in the context variable
sessionID.
Example 13.1, “Extracting the session ID” shows how the generated XML code of the event should look like.
Example 13.1. Extracting the session ID
...
<event actor="Client" name="getSessionID">
<context key="action">
<document>
<path>
<constant value="/html/body/form/@action"/>
</path>
</document>
</context>
<context key="indexOfSessionID">
<plugin name="Eval">
<argument name="expression">
<constant value="lastIndexOf(#{action},'jsessionid=',length(#{action}))"/>
</argument>
<argument key="action" name="string">
<reference key="action"/>
</argument>
<argument name="returnType">
<constant value="number"/>
</argument>
</plugin>
</context>
<context key="sessionID">
<plugin name="Eval">
<argument name="expression">
<constant value="substring(#{action}, #{indexOfSessionID}, length(#{action}))"/>
</argument>
<argument key="action" name="string">
<reference key="action"/>
</argument>
<argument key="indexOfSessionID" name="number">
<reference key="indexOfSessionID"/>
</argument>
<argument name="returnType">
<constant value="string"/>
</argument>
</plugin>
</context>
</event>
...
In the next step, the body with the data that was entered into the form plus the
data that was in hidden elements of the form, must be generated. Until now the sent
data was stored in the template
0002-Request.xml. This file does not contain xml, but the plain
message that was sent by the browser. Because it does not contain xml, it is not
possible to manipulate it with compositions, substitutions or omissions.
Because of this, the template will be disposed and the message will be generated on the fly by a filter that can encode the submitted form data in a proper way.
Before the template of the request will be deleted we will use it to find out what
data is sent. The content of the file
/template/generated/bookingExampleLogin/0002-Request.xml
will look approximately like this:
jsf_tree_64=H4sIAAAAAAAAAK2RwUrDQBCGx9JiFUHUg1dBsSKSYCmK9Gah 2pJSsFWQHso2WdPUZHfdTGy9ePPuA%2FgE0ifwCbx59SW8enZj0qYHoTl4GZ jh%2F%2Fj%2FmRl%2FQc6XcMalrRFBzD7VvIcbYlJftcJ1TIIOZ1pbUtpCGZ gYSNogjNhU7iTDCvcEZ5Rh9Xrz86X%2B%2FZGBbAeWumbfcS2p5lDvGMpCjy z02EKfsdDTWpQNWO2ak67iEt9HWDcG5J7oLmG2rvQOs5VsJZHVrDt4hEwHFr uhMypkozPDNHsDamJ5JAIZR%2F2Pa5TfxoX3tafn1wzASADAgh%2FGgCCsOd Xno14g7IdZRlrkNY2t9dFztXNVmgGKAA3Soy5CtutYJTFl9%2BawNabQNh0h Qj7wqWTEowm9O4duUN9XC0auxxMurMvR7ESk3uI3SYuakoZZhHrdkEsr4Qvp sqjv5ZTxYTEhD%2BaQ6iceYdZpgMhZjJfSB49xw2G3MXykqO05VJVLL7pRUa m3%2FlZf1q4cOrzgHIX4Afwk28eOAwAA&jsf_state_64=H4sIAAAAAAAAAJ VWTWwbRRQe%2F4Q4aVLy05ZWbanlhiBEWOfHSZyk0Dp10lis0yg2FTQHd7w7 sTdZ7y674%2ByGqFE59AICgRBVkYJA4sIBLlxAPYEQIJAAEYkLEhIHJIQEVO LCj0RhZnb9E3s3NSN5bM9%2BM%2B%2FN97739r3zK2gr66B%2FhV%2BDG5CT oVLgLubXkIBnXvrqyTd6jIdkPwCWBgAIlJ8G24COYPVXu0aGoYNetruMJZlb gEYxDbW29u8%2B%2BvjIlW8CwD8POmUVivNQwKqeAh24qCOjqMqipZ09x47p MkNk7iEfHwb9ql7g1vKqYXAGgiVuKXFhzqAGg01oYFnMfE%2FNPK8KUEbbf% 2FZe2Rn%2B6xc%2FCKZAqEhcElQR8aBdUMsK1jcx6GMXjtILRzNYl5TCDA9C 9G8ZFhA110HgG1CXoILZX0v7lwwMAAZ%2BkSEOYNC5kE3zudlEJnUegwPRol pCnFXEJZm61V9zK6HrcJOXDGw9u3vytc%2Fg6wHgS4GgIT2DGLk%2BM0jnKr E1so%2FUyMYgmJPEUQ3bF7a4VSggg5tX9RLFHPJgKYDBUUEtcUZZYTtkhA0u nVh%2BPJdKYhCKCZOr43lhzA2VWFriU3NJcpdo0zOppMlcEq3Csozn7cWBhK bJm1l1HSkLO18nZ5Qbb3ZR9ZiH93BDLto3evufTy0MlmiwoQaFIuJKm%2FZ9 dKSISF%2BXMEfB3AKZ6BWX2TLSZ6GBuIVUMjm3mDt%2FMZ1OLCZzqcWlJ7KZ XGYu2yzGDMK3ktdfvfHB%2B7EAc6aL8uKwRPTWRzmdzsmSsp4rSqKIlByVlf tgFN9LNoZYwEIuATvssuaELkZC11sfOh7mkax5xM1Hl%2B%2BrRQg5XpGVso F0BZaQi4cu1o9Xf3W4%2B1Y7r0FaWWRhDLoZQxVMa96KU9bee%2FkY0m9z3r YB5TIisRpskhUiOYwKUNi8RCGzkiKS3AT28DE1eW3LwgLbM2dppMAYkqrUb9 PBUftmBNkAMzuvrfzw4Z2X%2FQzWX4XVEG9dfy7z%2B%2BXdM37H%2Fv0V%2 B82HpUhaBG69nT%2F1wOc3qV3KwZQ5AHpOb1EGuQqNV52qVVdyNc0yHwEP12 VK%2BNzoyFB8JMzoejTSeETEYkqtap6dNauqMoLKl2H92rc7f%2F%2FmB77L Dt%2Bk0jgKfsznJXCigG5TEnFxOjwyOa5ZM16i6nZ7HdgynySH9NfLKE2oIV W1RaGjmLWvyP9fzk1pdGGwRdurk7Uk06BhmKoutpZkJ%2B6aZNXzSM2pZyeD BB1V06yCas3f%2BKh3mtHlcfZwgs1xNp%2Bp02LFVqtanBiKjzVosXqEo0Vq 4qz9dc5TYy2L7KC7yNoIUyP0PXjIRWVGi6GOk%2F7Dw899a2hbkyexxojOlj FWFdId2K8WCmnNp6m43dA86F0W0wgXVbGhLpIk54GvhMFx1tZYUSRHbWCtMM 1YnieTytmI3ls6j1VrYiPuvegXz%2F%2F4R%2BxFv1PqpomNU3XVsRFPy%2B P3y9vxrqdeuV0pj6YZAQdPb8lqQVI4Nu8RpN2eNXapTtP27u6ln34%2BuXWh 0qUCV%2BFODY0Mx8OkASUeUOnWm4qwatOdEFhvGObpopdq78mzyN69BjSLZK Lx3cqThqNeIhOtSQTmLaeIG2zeIIRH9iGclzDSofzCsat3VvAnwRrnJ0BIRw XSkiLdhW2K2aKOWOYgGNjD5tjw0PhklczKGRHPnqkyLOs%2Fpe6Gom4MAAA% 3D&jsf_viewid=%2Fhome.xhtml&_id2%3Ausername=petatest&_id2%3A password=veritpeta&_id2%3A_id14=Account+Login&_id2_SUBMIT=1& _id2%3A_link_hidden_=
This string is URL encoded and separated by semicolons. If the content is decoded and separated at the semicolons, the parameters are easier to determine:
This parameter is necessary for JSF, as it was not visible on the page it must have been in a hidden element. We'll get it's value from the submitted form.
As jsf_tree_64 this is stored in a hidden element, and we will get it's value from the form.
This is also a hidden input field. It could be set manually to
/home.xhtml, but can be taken from the form as
well.
This value, together with the _id2:password are the only parameters that can be filled into the form. It will be set manually to the account name that was set up in Section 12.1, “Preparations” .
The value of this form is also entered into the form, it will be specified manually.
This value comes from a hidden input field. It can be taken from the
form or set manually to
Account Login
This value comes from a hidden input field, and can be taken from an
input field or can be set manually to
1
The value of this parameter comes from a hidden input field and is empty, so it will be set to an empty string.
The encoded form data will be created with the filter
HTMLFormEncoder. This filter encodes the data of its parameters to
a body of a HTML request.
![]() | The HTMLFormEncoder filter |
|---|---|
|
Inserts an HTML Form parameter with name as specified by the key attribute and the provided value into the result message. |
The encoded parameters are attached to a template, so if a completely new
message should be generated, a new empty template must be created. To do this, press
the button, enter
emptyTemplate as name for the template and select the template folder of in the
PETA base path as parent folder. Press to
create the template, then delete all content of the file and save.
Now delete the old template from the event
0002-Request and add the new template
emptyTemplate to the event
0002-Request by dragging the
to the events operation.
Finally, the
HTMLFormEncoder output filter must be added to the operation of
0002-Request, by dragging the
button to the event. Then specify its arguments:
First add the
jsf_tree_64 parameter, by pressing the button, selecting
parameter, pressing entering
jsf_tree_64 and pressing
.
Then the correct value in the document must be assigned to the
argument's value: To do this press ,
select the
Path argument of the document, press and enter
/html/body/form/input[@id='jsf_tree_64']/@value as
path.
The the other parameters are added similar, if they are hidden input fields the
Path argument of the
document must be altered, otherwise, the document must be replaced
by a constant.
For the
jsf_state_64 parameter to the same as for the
jsf_tree_64 parameter, except that the name must be changed
do
jsf_state_64 and the path must be
/html/body/form/input[@id='jsf_state_64']/@value
.
As for the other hidden input values the value of
jsf_viewid should be extracted from the form. To do this, do
the same steps as above, but name the parameter
jsf_viewid and use for the path expression of the document
/html/body/form/input[@id='jsf_viewid']/@value.
For the
_id2:username parameter do the same as above, but change the
parameters key to
_id2:username and instead of adding a document add a
constant containing the user name that was specified in Section 12.1, “Preparations” .
For the
_id2:password do the same as in the previous paragraph, but
change the name to
_id2:password and enter as value the password that was
specified in Section 12.1, “Preparations”
.
For the
_id2:_id14 parameter do the same as for the
jsf_tree_64 parameter,but the name must be changed do
_id2:_id14 and the path must be
/html/body/form//input[@id='_id2:_id14']/@value .
Please note the double slash behind the
form node in the expression. This must be added, because the
hidden input field is enclosed with other tags.
The insertion of the
_id2_SUBMIT parameter is done as the other parameters for
hidden input fields. Set the name to
_id2_SUBMIT and the expression to
/html/body/form/input[@name='_id2_SUBMIT']/@value .
Please note here that the node is referenced with its name and not with its id,
as the other fields, because this field does not have an id.
Finally,, add the
_id2:_link_hidden_ parameter, as this parameter has no
value, this will not be taken from the form, but will be set to a constant empty
string. Do this as the insertion of the parameters for non-hidden
fields.
Example 13.2, “Generating the request dynamically” shows the code snippet of the test case that does create the request.
Example 13.2. Generating the request dynamically
...
<template id="emptyTemplate"/>
<outputfilter name="HtmlFormEncoder">
<argument key="jsf_tree_64" name="parameter">
<document>
<path>
<constant value="/html/body/form/input[@id='jsf_tree_64']/@value"/>
</path>
</document>
</argument>
<argument key="jsf_state_64" name="parameter">
<document>
<path>
<constant value="/html/body/form/input[@id='jsf_state_64']/@value"/>
</path>
</document>
</argument>
<argument key="jsf_viewid" name="parameter">
<document>
<path>
<constant value="/html/body/form/input[@id='jsf_viewid']/@value"/>
</path>
</document>
</argument>
<argument key="_id2:username" name="parameter">
<constant value="petatest"/>
</argument>
<argument key="_id2:password" name="parameter">
<constant value="veritpeta"/>
</argument>
<argument key="_id2:_id14" name="parameter">
<document>
<path>
<constant value="/html/body/form//input[@id='_id2:_id14']/@value"/>
</path>
</document>
</argument>
<argument key="_id2_SUBMIT" name="parameter">
<document>
<path>
<constant value="/html/body/form/input[@name='_id2_SUBMIT']/@value"/>
</path>
</document>
</argument>
<argument key="_id2:_link_hidden_" name="parameter">
<constant value=""/>
</argument>
</outputfilter>
...
Now the test case is ready to run. Try out what has been built: Let the test case
run in the
Simulated and the
Real setup. It will succeed as expected. Try to run the test case with
a wrong password. The password can be changed more comfortable as in the first try,
just by changing the value of the parameter
_id2:password in the
HTMLEncoder filter in event
0002-Response. When the test case is run now, it will fail as
expected.
At the beginning of this chapter we started with a recorded test case. This test case did run successful, but it was not very smart: It just asserted the correct status codes of the returned pages.
When the password was changed as an experiment we detected this drawback, and we refined the test case in that way the wrong password was detected.
Then we detected that there are more places where the test case does not behave as in the normal setting, so we started to reuse the data that was sent back in a form for the next request, that was built dynamically.
As a result we have a test case that imitates the normal behavior of a real client.