Login | Register
My pages Projects Community openCollabNet

Discussions > dev > Re: [maxq-dev] Obese scripts

maxq
Discussion topic

Hide all messages in topic

All messages in topic

Re: [maxq-dev] Obese scripts

Author bitter
Full name lee
Date 2004-09-29 18:49:38 PDT
Message Oliver,
 
 Not now. but i'll try it this week.
 
regards
 
john

Oliver Bock <oliver at g7 dot org> wrote:
On 28/09/2004, at 16:37, john lee wrote:
> in fact , i wish maxq run as a junit runner.

John,

Have you tried using MaxQ tests in a JUnit test runner?


Oliver


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org




--------------------​-------------
Do You Yahoo!?
150万曲MP3疯狂搜,带您闯入音乐殿堂
美女明星应有尽有,搜遍美图、艳图和酷图
1G就是1000兆,雅虎电邮自助扩容!
Attachments

Re: [maxq-dev] Obese scripts

Author oliverbock
Full name Oliver Bock
Date 2004-09-29 14:32:39 PDT
Message On 28/09/2004, at 16:37, john lee wrote:
> in fact , i wish maxq run as a junit runner.

John,

Have you tried using MaxQ tests in a JUnit test runner?


    Oliver


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author hdara at primavera dot com
Full name hdara at primavera dot com
Date 2004-09-29 10:16:43 PDT
Message That could be possible, and could be a good solution. Thanks for the idea.

Hari

Matt Avery <mavery at einnovation dot com> wrote on 09/29/2004 07:49:04 AM:

> I just re-read this and realized how confusing it is... what I meant to
> say was
>
> "configure your new version of MaxQ as an http proxy to re-record the
> script with the new generator?".
>
> Matt Avery wrote:
> > Couldn't you run your old scripts with your old version of MaxQ and
> > insert your new version of MaxQ to re-record the script with the new
> > generator?
> >
> > hdara at primavera dot com wrote:
> >
> >> Oliver Bock <oliver at g7 dot org> wrote on 09/28/2004 02:11:22 PM:
> >>
> >>
> >>> Nobody worried about making scripts backwards compatible when the
> >>> switch was made to HttpClient, and I'm not inclined to do so in this

> >>> case either because I prefer to keep the code as simple as possible.

> >>> (Your suggestion would require me to overload at least five
different
> >>> functions.) The change I've made takes scripts to a canonical form
> >>> that will not require changing again. However I will be happy to
> >>> provide a PERL script for fixing existing scripts if anyone wants
it.
> >>>
> >>>
> >>> Oliver
> >>
> >>
> >>
> >> I wasn't even a MaxQ user when this change happened so I can't really

> >> comment on it, but whenever I make any change, I keep this in mind,
> >> because without an upgrade path there is no tool that will be
> >> successful. Imagine users having to constantly rewrite/fix their
> >> scripts just to upgrade to a version which has a fix for a bug that
> >> they are bothered about. All that I mean is that the runtime should
be
> >> capable of executing old scripts, even thought current script format
> >> is different. Of course, this is not a hard and fast rule, and
> >> providing a perl script (which will work, unlike the one which exists

> >> to split large scripts) can be a reasonable solution.
> >>
> >> Thanks,
> >> Hari
> >>
> >>
> >>> ---
> >>> On 29/09/2004, at 03:29, hdara at primavera dot com wrote:
> >>>
> >>>
> >>>> Oliver,
> >>>>
> >>>> I am wondering if any of your changes (such as using pythong arryas
> >>>> instead of ArrayList) are going to break existing scripts. What I
> >>>> recommend is to leave the original methods just like that and add
new
> >>>> methods for your changes (such as adding a method that takes arrays
> >>>> instead of changing the existing one). This will make it easier for

> >>>> uses
> >>>> to upgrade (including me).
> >>>>
> >>>> Thanks,
> >>>> Hari
> >>
> >>
> >>
> >>
> >> --------------------​--------------------​--------------------​---------
> >> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> >> For additional commands, e-mail: dev-help at maxq dot tigris dot org
> >>
> >>
> >>
> >
>
> --
> Matthew Avery
> Senior Developer
> (513) 470-5316
> http://www.einnovation.com/
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author avery1701
Full name Matt Avery
Date 2004-09-29 07:49:04 PDT
Message I just re-read this and realized how confusing it is... what I meant to
say was

"configure your new version of MaxQ as an http proxy to re-record the
script with the new generator?".

Matt Avery wrote:
> Couldn't you run your old scripts with your old version of MaxQ and
> insert your new version of MaxQ to re-record the script with the new
> generator?
>
> hdara at primavera dot com wrote:
>
>> Oliver Bock <oliver at g7 dot org> wrote on 09/28/2004 02:11:22 PM:
>>
>>
>>> Nobody worried about making scripts backwards compatible when the
>>> switch was made to HttpClient, and I'm not inclined to do so in this
>>> case either because I prefer to keep the code as simple as possible.
>>> (Your suggestion would require me to overload at least five different
>>> functions.) The change I've made takes scripts to a canonical form
>>> that will not require changing again. However I will be happy to
>>> provide a PERL script for fixing existing scripts if anyone wants it.
>>>
>>>
>>> Oliver
>>
>>
>>
>> I wasn't even a MaxQ user when this change happened so I can't really
>> comment on it, but whenever I make any change, I keep this in mind,
>> because without an upgrade path there is no tool that will be
>> successful. Imagine users having to constantly rewrite/fix their
>> scripts just to upgrade to a version which has a fix for a bug that
>> they are bothered about. All that I mean is that the runtime should be
>> capable of executing old scripts, even thought current script format
>> is different. Of course, this is not a hard and fast rule, and
>> providing a perl script (which will work, unlike the one which exists
>> to split large scripts) can be a reasonable solution.
>>
>> Thanks,
>> Hari
>>
>>
>>> ---
>>> On 29/09/2004, at 03:29, hdara at primavera dot com wrote:
>>>
>>>
>>>> Oliver,
>>>>
>>>> I am wondering if any of your changes (such as using pythong arryas
>>>> instead of ArrayList) are going to break existing scripts. What I
>>>> recommend is to leave the original methods just like that and add new
>>>> methods for your changes (such as adding a method that takes arrays
>>>> instead of changing the existing one). This will make it easier for
>>>> uses
>>>> to upgrade (including me).
>>>>
>>>> Thanks,
>>>> Hari
>>
>>
>>
>>
>> --------------------​--------------------​--------------------​---------
>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>
>>
>>
>

--
Matthew Avery
Senior Developer
(513) 470-5316
http://www.einnovation.com/

--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author avery1701
Full name Matt Avery
Date 2004-09-29 07:07:51 PDT
Message Couldn't you run your old scripts with your old version of MaxQ and
insert your new version of MaxQ to re-record the script with the new
generator?

hdara at primavera dot com wrote:
> Oliver Bock <oliver at g7 dot org> wrote on 09/28/2004 02:11:22 PM:
>
>
>>Nobody worried about making scripts backwards compatible when the
>>switch was made to HttpClient, and I'm not inclined to do so in this
>>case either because I prefer to keep the code as simple as possible.
>>(Your suggestion would require me to overload at least five different
>>functions.) The change I've made takes scripts to a canonical form
>>that will not require changing again. However I will be happy to
>>provide a PERL script for fixing existing scripts if anyone wants it.
>>
>>
>> Oliver
>
>
> I wasn't even a MaxQ user when this change happened so I can't really
> comment on it, but whenever I make any change, I keep this in mind,
> because without an upgrade path there is no tool that will be successful.
> Imagine users having to constantly rewrite/fix their scripts just to
> upgrade to a version which has a fix for a bug that they are bothered
> about. All that I mean is that the runtime should be capable of executing
> old scripts, even thought current script format is different. Of course,
> this is not a hard and fast rule, and providing a perl script (which will
> work, unlike the one which exists to split large scripts) can be a
> reasonable solution.
>
> Thanks,
> Hari
>
>
>>---
>>On 29/09/2004, at 03:29, hdara at primavera dot com wrote:
>>
>>
>>>Oliver,
>>>
>>>I am wondering if any of your changes (such as using pythong arryas
>>>instead of ArrayList) are going to break existing scripts. What I
>>>recommend is to leave the original methods just like that and add new
>>>methods for your changes (such as adding a method that takes arrays
>>>instead of changing the existing one). This will make it easier for
>>>uses
>>>to upgrade (including me).
>>>
>>>Thanks,
>>>Hari
>
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>
>

--
Matthew Avery
Senior Developer
(513) 470-5316
http://www.einnovation.com/

--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author oliverbock
Full name Oliver Bock
Date 2004-09-28 16:10:32 PDT
Message Hari,

In principal I agree with what you are saying about backward
compatibility, but I believe that MaxQ's situation is an exception.
 From the traffic I see on these lists, I reckon that MaxQ has perhaps
half a dozen serious users who might need to upgrade their scripts.

When I first started looking at MaxQ, CVS contained broken, crufty code
that had been modified and added to by many people, but without any
guiding vision. It almost stopped me working on the project at all.
Ryan got the thing working again, and I've made it my mission to clean
it up. My focus is on getting MaxQ's code base simple and clean. If
its simple and well commented then developers will be willing to work
on it. I believe this lack of users is MaxQ's most important
challenge.


    Oliver

--
On 29/09/2004, at 07:29, hdara at primavera dot com wrote:
> Oliver Bock <oliver at g7 dot org> wrote on 09/28/2004 02:11:22 PM:
>> Nobody worried about making scripts backwards compatible when the
>> switch was made to HttpClient, and I'm not inclined to do so in this
>> case either because I prefer to keep the code as simple as possible.
>> (Your suggestion would require me to overload at least five different
>> functions.) The change I've made takes scripts to a canonical form
>> that will not require changing again. However I will be happy to
>> provide a PERL script for fixing existing scripts if anyone wants it.
>
> I wasn't even a MaxQ user when this change happened so I can't really
> comment on it, but whenever I make any change, I keep this in mind,
> because without an upgrade path there is no tool that will be
> successful.
> Imagine users having to constantly rewrite/fix their scripts just to
> upgrade to a version which has a fix for a bug that they are bothered
> about. All that I mean is that the runtime should be capable of
> executing
> old scripts, even thought current script format is different. Of
> course,
> this is not a hard and fast rule, and providing a perl script (which
> will
> work, unlike the one which exists to split large scripts) can be a
> reasonable solution.
>>
>> ---
>> On 29/09/2004, at 03:29, hdara at primavera dot com wrote:
>>> Oliver,
>>>
>>> I am wondering if any of your changes (such as using pythong arryas
>>> instead of ArrayList) are going to break existing scripts. What I
>>> recommend is to leave the original methods just like that and add new
>>> methods for your changes (such as adding a method that takes arrays
>>> instead of changing the existing one). This will make it easier for
>>> uses
>>> to upgrade (including me).
>>>
>>> Thanks,
>>> Hari


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author hdara at primavera dot com
Full name hdara at primavera dot com
Date 2004-09-28 14:29:05 PDT
Message Oliver Bock <oliver at g7 dot org> wrote on 09/28/2004 02:11:22 PM:

> Nobody worried about making scripts backwards compatible when the
> switch was made to HttpClient, and I'm not inclined to do so in this
> case either because I prefer to keep the code as simple as possible.
> (Your suggestion would require me to overload at least five different
> functions.) The change I've made takes scripts to a canonical form
> that will not require changing again. However I will be happy to
> provide a PERL script for fixing existing scripts if anyone wants it.
>
>
> Oliver

I wasn't even a MaxQ user when this change happened so I can't really
comment on it, but whenever I make any change, I keep this in mind,
because without an upgrade path there is no tool that will be successful.
Imagine users having to constantly rewrite/fix their scripts just to
upgrade to a version which has a fix for a bug that they are bothered
about. All that I mean is that the runtime should be capable of executing
old scripts, even thought current script format is different. Of course,
this is not a hard and fast rule, and providing a perl script (which will
work, unlike the one which exists to split large scripts) can be a
reasonable solution.

Thanks,
Hari

>
> ---
> On 29/09/2004, at 03:29, hdara at primavera dot com wrote:
>
> > Oliver,
> >
> > I am wondering if any of your changes (such as using pythong arryas
> > instead of ArrayList) are going to break existing scripts. What I
> > recommend is to leave the original methods just like that and add new
> > methods for your changes (such as adding a method that takes arrays
> > instead of changing the existing one). This will make it easier for
> > uses
> > to upgrade (including me).
> >
> > Thanks,
> > Hari


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Regular expressions in Jython?

Author fcohen
Full name Frank Cohen
Date 2004-09-28 14:13:34 PDT
Message Thanks. I get confused too. And often... :-)




On Sep 28, 2004, at 2:08 PM, Oliver Bock wrote:

>> I am unable to get jUnit's TestCase.assert to handle a Jython re
>> expression. When I process:
>>
>> assert( re.match( "He", "Hello" ) )
>>
>> I get an exception: 1st arg cannot be coerced to boolean.
>>
>> Any ideas why?
>
> Probably it should be
>
> assert(re.match("He", "Hello") is not None)
>
> I'm using too many languages at the moment and I can get a bit
> confused.
>
>
> Oliver
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>
---
Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
Author of "Java Testing and Design: From Unit Tests to Automated Web
Tests"
from Prentice Hall, details at http://thebook.pushtotest.com


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author oliverbock
Full name Oliver Bock
Date 2004-09-28 14:11:22 PDT
Message Nobody worried about making scripts backwards compatible when the
switch was made to HttpClient, and I'm not inclined to do so in this
case either because I prefer to keep the code as simple as possible.
(Your suggestion would require me to overload at least five different
functions.) The change I've made takes scripts to a canonical form
that will not require changing again. However I will be happy to
provide a PERL script for fixing existing scripts if anyone wants it.


    Oliver

---
On 29/09/2004, at 03:29, hdara at primavera dot com wrote:

> Oliver,
>
> I am wondering if any of your changes (such as using pythong arryas
> instead of ArrayList) are going to break existing scripts. What I
> recommend is to leave the original methods just like that and add new
> methods for your changes (such as adding a method that takes arrays
> instead of changing the existing one). This will make it easier for
> uses
> to upgrade (including me).
>
> Thanks,
> Hari
>
> Hari Krishna Dara/SF/DE/Primavera wrote on 09/27/2004 10:47:15 PM:
>
>> Oliver Bock <oliver at g7 dot org> wrote on 09/27/2004 09:00:49 PM:
>>
>>>> - I added the switchable driver class (and validator class), such
> that
>>>> the
>>>> same script can be dynamically run by a different driver. The idea
> is
>>>> that
>>>> when we extend the usefulness of the tool such that even QA is
> tempted
>>>> to
>>>> use it, we want them to have an option to visualize the execution of
>
>>>> the
>>>> script. If we can design a driver that can redirect the calls to a
>>>> browser
>>>> some how, then the same script can be rerun with a different driver
> to
>>>> see
>>>> the execution in action. If you just switch the generator during the
>>>> record time, you don't get the same value. If you can come up with a
>
>>>> more
>>>> elegant solution to this problem, I have no problem removing it.
>>>> - I am OK with the rest of the changes. Leaving the logger might not
>
>>>> be a
>>>> bad idea.
>>>
>>> Are replacement classes that take advantage of these features
> available?
>>>
>
>> Nope, they are not available right now, but would love to work on
>> them if time permits.
>>
>> Hari
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Regular expressions in Jython?

Author oliverbock
Full name Oliver Bock
Date 2004-09-28 14:08:27 PDT
Message > I am unable to get jUnit's TestCase.assert to handle a Jython re
> expression. When I process:
>
> assert( re.match( "He", "Hello" ) )
>
> I get an exception: 1st arg cannot be coerced to boolean.
>
> Any ideas why?

Probably it should be

  assert(re.match("He", "Hello") is not None)

I'm using too many languages at the moment and I can get a bit confused.


    Oliver


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author hdara at primavera dot com
Full name hdara at primavera dot com
Date 2004-09-28 10:29:04 PDT
Message Oliver,

I am wondering if any of your changes (such as using pythong arryas
instead of ArrayList) are going to break existing scripts. What I
recommend is to leave the original methods just like that and add new
methods for your changes (such as adding a method that takes arrays
instead of changing the existing one). This will make it easier for uses
to upgrade (including me).

Thanks,
Hari

Hari Krishna Dara/SF/DE/Primavera wrote on 09/27/2004 10:47:15 PM:

> Oliver Bock <oliver at g7 dot org> wrote on 09/27/2004 09:00:49 PM:
>
> > > - I added the switchable driver class (and validator class), such
that
> > > the
> > > same script can be dynamically run by a different driver. The idea
is
> > > that
> > > when we extend the usefulness of the tool such that even QA is
tempted
> > > to
> > > use it, we want them to have an option to visualize the execution of

> > > the
> > > script. If we can design a driver that can redirect the calls to a
> > > browser
> > > some how, then the same script can be rerun with a different driver
to
> > > see
> > > the execution in action. If you just switch the generator during the
> > > record time, you don't get the same value. If you can come up with a

> > > more
> > > elegant solution to this problem, I have no problem removing it.
> > > - I am OK with the rest of the changes. Leaving the logger might not

> > > be a
> > > bad idea.
> >
> > Are replacement classes that take advantage of these features
available?
> >

> Nope, they are not available right now, but would love to work on
> them if time permits.
>
> Hari

--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author bitter
Full name lee
Date 2004-09-27 23:38:33 PDT
Message hi all,
 
    i prefer the log method than print statement, because log method just give a choice, that's enough.
 
    as a tester, when run tests, i only care if the tests have passed or not,
and only need information when something wrong, or some errors occur.
    
    for my opinin, maxq is not a tool that just record and playback, but generate a "test" for users. so i don't think print statement is necessary . in fact , i wish maxq run as a junit runner.
 
   regards
 
      john
 
    
    

Oliver Bock <oliver at g7 dot org> wrote:
Frank,

Sorry, now I see that you have provided an example. I like that your
logging is self-contained. Perhaps we should include a self.log()
function in the HttpBaseTest that will output just like "print"
normally, but which can be turned off by calling another function, like
self.suppressLog() or self.setLogLevel(). This way it won't get in the
way of normal users, but is available for people who want it.

Your script looks reasonable as a JUnit test, except that it is not
derived from TestCase. I have not used MaxQ scripts as real JUnit
tests, but since they are derived form TestCase it should just be a
matter of compiling them into Java classes (jythonc) and including
maxq.jar in the classpath.


Oliver

--
On 28/09/2004, at 08:21, Frank Cohen wrote:

> One other thing... In an earlier email I proposed using a log-level
> variable to determine which information get printed to the console as
> the test script operations. Did you consider this? Below is my sample
> script that incorporates the log-level.
>
> -Frank
>
>
> A few TestMaker users have asked me about support of jUnit. It seems
> to me that the TestMaker recorder should be outputing tests that
> conform to a jUnit TestCase. Below is a script that accomplishes this.
> I am seeking your input, feedback and criticism of this script before
> I make any changes to the recorder.
>
> '''
> Agent name: mytest.py
> Created by: TestMaker Agent Recorder
> Created on:
>
> Purpose:
> Calculates a Transaction-Per-Second index for a
> Web-enabled HTTP application
>
> For details on TestMaker see http://www.pushtotest.com
> '''
>
> from com.pushtotest.tool.​protocolhandler import ProtocolHandler,
> HTTPProtocol
> from com.pushtotest.tool.response import Response
> from java.util import Date
> from java.lang import Exception
> from junit.framework import TestCase
>
> '''
> The test class implements the steps recorded while watching a user
> operate the application using a browser.
> '''
>
> class mytest:
>
> def __init__( self, loglvl = 0 ):
> ''' Initialization '''
> self.loglevel = loglvl
> if self.loglevel > 0:
> print "mytest: Initialize"
> self.steps = 0 # Counter of steps in transaction
> self.http = ProtocolHandler.getP​rotocol("http")
>
> def setUp( self ):
> ''' Add any needed set-up code here. '''
> if self.loglevel > 0:
> print "mytest: setUp"
> pass
>
> def runTest( self ):
> ''' Run the test '''
> if self.loglevel > 0:
> print "mytest: runTest"
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder?
> file=file1.html''' )
> self.http.setType( HTTPProtocol.GET )
> self.response = self.http.connect()
> TestCase.assertEquals("Step 1 failed while trying to get: '" +
> \
> self.http.getURL("http"), 200, self.response.getResponseCode()
> )
> self.logit()
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder''' )
> self.http.setType( HTTPProtocol.POST )
> self.body = ProtocolHandler.getBody( 'http' )
> self.http.setBody( self.body )
> self.body.addParamet​er('''firstname''', '''frank''')
> self.body.addParamet​er('''lastname''', '''cohen''')
> self.body.addParamet​er('''phone''', '''''')
> self.body.addParamet​er('''account''', '''''')
> self.body.addParamet​er('''amount''', '''''')
> self.body.addParamet​er('''Transfer''', '''Transfer Funds''')
> self.response = self.http.connect()
> TestCase.assertEquals("Step 2 failed while trying to post: '"
> + self.http.getURL("http"), 200, self.response.getResponseCode() )
> self.logit()
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder?
> file=file3.html''' )
> self.http.setType( HTTPProtocol.GET )
> self.response = self.http.connect()
> TestCase.assertEquals("Step 3 failed while trying to get: '" +
> self.http.getURL("http"), 200, self.response.getResponseCode() )
> self.logit()
>
> def logit( self ):
> self.steps += 1
> if self.loglevel > 1:
> print "mytest: runTest, step", self.steps
>
> def tearDown( self ):
> ''' Add any needed code to end the test here. '''
>
> def setLoglevel( self, loglv ):
> self.loglevel = loglv
> def getLogLevel( self ):
> return self.loglevel
> def getSteps( self ):
> return self.steps
>
> # Code to instantiate and run the test
>
> if __name__ == 'main':
>
> print "===================​====================​================"
> print " myTest.py: Functional test of a Web application "
> print "===================​====================​================"
> print "Test recorded by TestMaker from http://www.pushtotest.com"
> print
>
> test = mytest( 2 ) # Value determines logging level, 0 =
> off, 1 = Major messages only, 2 = Step details
> test.runTest()
>
> print "Test executed", test.getSteps(), "steps."
> print "Test completed."
>
>
> I am open to your input, feedback and criticism of the above script.
> Please let me know your thoughts. Thanks, in advance.
>
> -Frank
>
>
>
>
> On Sep 27, 2004, at 3:08 PM, Frank Cohen wrote:
>
>> Nice summary of the problem and solution. I urge you to consider two
>> changes to your proposal.
>>
>> 1) It's nice to see how many ideas are being passed around the MaxQ
>> project. At this rate MaxQ is going to outgrow a one-size-fits-all
>> format for the recorded scripts. For example, I really want the
>> logging to stay. I propose adding a Preferences command that allows
>> users to choose recording options.
>>
>> 2) Add a regex way to validate http response codes.
>>
>> -Frank
>>
>>
>> On Sep 27, 2004, at 2:51 PM, Oliver Bock wrote:
>>
>>> I'm concerned that the scripts being generated by MaxQ are complex
>>> and too large. While I mainly use my new "compact" scripts, I do
>>> think that the old style is still useful for new users because they
>>> can see what is happening. Its simplicity is also useful for people
>>> who are only generating a few scripts and don't mind hand editing
>>> them. Here is a script that submits a single form with four
>>> parameters:
>>>
>>> # Generated by MaxQ
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> from sys import *
>>>
>>> from com.bitmechanic.maxq import Config
>>> global driverPkg, validatorPkg, logger
>>> if __name__ == 'main':
>>> driverPkg = Config.getDriverPkgName()
>>> validatorPkg = Config.getValidatorPkgName()
>>>
>>> # Determine the driver for this testcase.
>>> exec 'from '+driverPkg+' import HttpTestCase'
>>>
>>> # Determine the validator for this testcase.
>>> exec 'from '+validatorPkg+' import Validator'
>>>
>>> # imports
>>> from junit.textui import TestRunner
>>> from java.lang import *
>>> from java.io import *
>>> from java.util import *
>>> from org.apache.commons.httpclient import NameValuePair
>>>
>>>
>>> # definition of test class
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> class MaxQTest(HttpTestCase):
>>> def __init__(self, testName):
>>> HttpTestCase.__init__(self, testName)
>>>
>>> # Add setup code here. See Junit javadoc.
>>> def setUp(self):
>>> pass
>>>
>>> # Add tearDown code here. See Junit javadoc.
>>> def tearDown(self):
>>> pass
>>>
>>> # Runs the test
>>> def runTest(self):
>>> self.doTest()
>>>
>>> # Test script
>>> def doTest(self):
>>> self.currentParams = ArrayList()
>>> self.currentParams.a​dd(NameValuePair('''​scope''',
>>> '''projectAndSubs'''))
>>> self.currentParams.a​dd(NameValuePair('''​resultsPerPage''',
>>> '''40'''))
>>> self.currentParams.a​dd(NameValuePair('''​query''',
>>> '''ghjghj'''))
>>> self.currentParams.a​dd(NameValuePair('''​Button''', '''Go'''))
>>> print "Testing URL: %s" %
>>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>>> list = self.currentParams
>>> Validator.validateRequest(self, self.getMethod(), "get",
>>> self.currentUrl, self.currentParams)
>>> self.get(self.currentUrl, list)
>>> print "Response code: %s" % self.getMethod().get​StatusCode()
>>> self.assertEquals("Assert number 2 failed", 200,
>>> self.getMethod().get​StatusCode())
>>> Validator.validateRe​sponse(self, self.getMethod(),
>>> self.currentUrl, self.currentParams)
>>>
>>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>>
>>>
>>> ####################​####################​##
>>>
>>> # Code to load and run the test
>>> if __name__ == 'main':
>>> logger = Config.getTestLogger()
>>> test = MaxQTest("MaxQTest")
>>> test.Run()
>>>
>>> SIXTY SIX LINES!!! When it's simple, Jython/Python is a language
>>> that inexperienced programmers can understand and get started on
>>> quickly. Presumably this is why it was chosen for MaxQ. But this?
>>> Can you imagine the reaction of most people when they download MaxQ
>>> and it produces this stuff? They go elsewhere.
>>>
>>> I propose these changes:
>>>
>>> - We switch from encoding parameters in an ArrayList of
>>> NameValuePairs to a Jython array of tuples. This will make the
>>> scripts more concise and will stop them being tied to HttpClient.
>>> e.g.
>>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>>> ('''resultsPerPage''', '''40'''),
>>> ('''query''', '''ghjghj'''),
>>> ('''Button''', '''Go''')]
>>>
>>> - We remove the empty setUp() and tearDown() functions. They are
>>> documented and so need not be included in every script. We can also
>>> include some sample scripts showing how to use them.
>>>
>>> - We get rid of doTest() and put recorded code in runTest(). It is
>>> trivial for the user to break the code into multiple functions if
>>> this is what she desires. Again, examples will assist.
>>>
>>> - We stop using esoteric syntax to allow Config to override the
>>> class for HttpTestCase and Validator. No new MaxQ user will want to
>>> do this, rather they will wonder: "What they hell is this? This
>>> doesn't appear in the Python tutorial!" Now that the
>>> JythonGenerator code is much simpler, it would be easy to derive a
>>> new class from JythonGenerator to change these base classes for
>>> whichever sophisticated users wish it.
>>>
>>> - We stop loading a logger. We do not want people to use a logger
>>> because it will stop output going to stdout/stderr and therefore it
>>> will not appear in MaxQ's run window. "print" is obviously easier
>>> for a user to understand, and that's what the script uses anyway.
>>>
>>> We would end up with a script like this:
>>>
>>> # Generated by MaxQ
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> from com.bitmechanic.maxq import Validator
>>>
>>> class MaxQTest(HttpTestCase):
>>> def runTest(self):
>>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>>> ('''resultsPerPage''', '''40'''),
>>> ('''query''', '''ghjghj'''),
>>> ('''Button''', '''Go''')]
>>> print "Testing URL: %s" %
>>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>>> Validator.validateRequest(self, self.getMethod(), "get",
>>> self.currentUrl, self.currentParams)
>>> self.get(self.currentUrl, self.currentParams)
>>> print "Response code: %s" % self.getMethod().get​StatusCode()
>>> self.assertEquals("Assert number 2 failed", 200,
>>> self.getMethod().get​StatusCode())
>>> Validator.validateRe​sponse(self, self.getMethod(),
>>> self.currentUrl, self.currentParams)
>>>
>>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>>
>>>
>>> # Code to load and run the test
>>> if __name__ == 'main':
>>> test = MaxQTest("MaxQTest")
>>> test.Run()
>>>
>>>
>>> Oliver
>>>
>>>
>>> --------------------​--------------------​--------------------​---------
>>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>>
>>>
>> ---
>> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374
>> 7426
>> Author of "Java Testing and Design: From Unit Tests to Automated Web
>> Tests"
>> from Prentice Hall, details at http://thebook.pushtotest.com
>>
>>
>> --------------------​--------------------​--------------------​---------
>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>
>>
> ---
> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
> Author of "Java Testing and Design: From Unit Tests to Automated Web
> Tests"
> from Prentice Hall, details at http://thebook.pushtotest.com
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org




--------------------​-------------
Do You Yahoo!?
150万曲MP3疯狂搜,带您闯入音乐殿堂
美女明星应有尽有,搜遍美图、艳图和酷图
1G就是1000兆,雅虎电邮自助扩容!
Attachments

Re: [maxq-dev] Obese scripts

Author bitter
Full name lee
Date 2004-09-27 23:37:48 PDT
Message hi all,
 
    i prefer the log method than print statement, because log method just give a choice, that's enough.
 
    as a tester, when run tests, i only care if the tests have passed or not,
and only need information when something wrong, or some errors occur.
    
    for my opinin, maxq is not a tool that just record and playback, but generate a "test" for users. so i don't think print statement is necessary . in fact , i wish maxq run as a junit runner.
 
    
    

Oliver Bock <oliver at g7 dot org> wrote:
Frank,

Sorry, now I see that you have provided an example. I like that your
logging is self-contained. Perhaps we should include a self.log()
function in the HttpBaseTest that will output just like "print"
normally, but which can be turned off by calling another function, like
self.suppressLog() or self.setLogLevel(). This way it won't get in the
way of normal users, but is available for people who want it.

Your script looks reasonable as a JUnit test, except that it is not
derived from TestCase. I have not used MaxQ scripts as real JUnit
tests, but since they are derived form TestCase it should just be a
matter of compiling them into Java classes (jythonc) and including
maxq.jar in the classpath.


Oliver

--
On 28/09/2004, at 08:21, Frank Cohen wrote:

> One other thing... In an earlier email I proposed using a log-level
> variable to determine which information get printed to the console as
> the test script operations. Did you consider this? Below is my sample
> script that incorporates the log-level.
>
> -Frank
>
>
> A few TestMaker users have asked me about support of jUnit. It seems
> to me that the TestMaker recorder should be outputing tests that
> conform to a jUnit TestCase. Below is a script that accomplishes this.
> I am seeking your input, feedback and criticism of this script before
> I make any changes to the recorder.
>
> '''
> Agent name: mytest.py
> Created by: TestMaker Agent Recorder
> Created on:
>
> Purpose:
> Calculates a Transaction-Per-Second index for a
> Web-enabled HTTP application
>
> For details on TestMaker see http://www.pushtotest.com
> '''
>
> from com.pushtotest.tool.​protocolhandler import ProtocolHandler,
> HTTPProtocol
> from com.pushtotest.tool.response import Response
> from java.util import Date
> from java.lang import Exception
> from junit.framework import TestCase
>
> '''
> The test class implements the steps recorded while watching a user
> operate the application using a browser.
> '''
>
> class mytest:
>
> def __init__( self, loglvl = 0 ):
> ''' Initialization '''
> self.loglevel = loglvl
> if self.loglevel > 0:
> print "mytest: Initialize"
> self.steps = 0 # Counter of steps in transaction
> self.http = ProtocolHandler.getP​rotocol("http")
>
> def setUp( self ):
> ''' Add any needed set-up code here. '''
> if self.loglevel > 0:
> print "mytest: setUp"
> pass
>
> def runTest( self ):
> ''' Run the test '''
> if self.loglevel > 0:
> print "mytest: runTest"
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder?
> file=file1.html''' )
> self.http.setType( HTTPProtocol.GET )
> self.response = self.http.connect()
> TestCase.assertEquals("Step 1 failed while trying to get: '" +
> \
> self.http.getURL("http"), 200, self.response.getResponseCode()
> )
> self.logit()
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder''' )
> self.http.setType( HTTPProtocol.POST )
> self.body = ProtocolHandler.getBody( 'http' )
> self.http.setBody( self.body )
> self.body.addParamet​er('''firstname''', '''frank''')
> self.body.addParamet​er('''lastname''', '''cohen''')
> self.body.addParamet​er('''phone''', '''''')
> self.body.addParamet​er('''account''', '''''')
> self.body.addParamet​er('''amount''', '''''')
> self.body.addParamet​er('''Transfer''', '''Transfer Funds''')
> self.response = self.http.connect()
> TestCase.assertEquals("Step 2 failed while trying to post: '"
> + self.http.getURL("http"), 200, self.response.getResponseCode() )
> self.logit()
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder?
> file=file3.html''' )
> self.http.setType( HTTPProtocol.GET )
> self.response = self.http.connect()
> TestCase.assertEquals("Step 3 failed while trying to get: '" +
> self.http.getURL("http"), 200, self.response.getResponseCode() )
> self.logit()
>
> def logit( self ):
> self.steps += 1
> if self.loglevel > 1:
> print "mytest: runTest, step", self.steps
>
> def tearDown( self ):
> ''' Add any needed code to end the test here. '''
>
> def setLoglevel( self, loglv ):
> self.loglevel = loglv
> def getLogLevel( self ):
> return self.loglevel
> def getSteps( self ):
> return self.steps
>
> # Code to instantiate and run the test
>
> if __name__ == 'main':
>
> print "===================​====================​================"
> print " myTest.py: Functional test of a Web application "
> print "===================​====================​================"
> print "Test recorded by TestMaker from http://www.pushtotest.com"
> print
>
> test = mytest( 2 ) # Value determines logging level, 0 =
> off, 1 = Major messages only, 2 = Step details
> test.runTest()
>
> print "Test executed", test.getSteps(), "steps."
> print "Test completed."
>
>
> I am open to your input, feedback and criticism of the above script.
> Please let me know your thoughts. Thanks, in advance.
>
> -Frank
>
>
>
>
> On Sep 27, 2004, at 3:08 PM, Frank Cohen wrote:
>
>> Nice summary of the problem and solution. I urge you to consider two
>> changes to your proposal.
>>
>> 1) It's nice to see how many ideas are being passed around the MaxQ
>> project. At this rate MaxQ is going to outgrow a one-size-fits-all
>> format for the recorded scripts. For example, I really want the
>> logging to stay. I propose adding a Preferences command that allows
>> users to choose recording options.
>>
>> 2) Add a regex way to validate http response codes.
>>
>> -Frank
>>
>>
>> On Sep 27, 2004, at 2:51 PM, Oliver Bock wrote:
>>
>>> I'm concerned that the scripts being generated by MaxQ are complex
>>> and too large. While I mainly use my new "compact" scripts, I do
>>> think that the old style is still useful for new users because they
>>> can see what is happening. Its simplicity is also useful for people
>>> who are only generating a few scripts and don't mind hand editing
>>> them. Here is a script that submits a single form with four
>>> parameters:
>>>
>>> # Generated by MaxQ
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> from sys import *
>>>
>>> from com.bitmechanic.maxq import Config
>>> global driverPkg, validatorPkg, logger
>>> if __name__ == 'main':
>>> driverPkg = Config.getDriverPkgName()
>>> validatorPkg = Config.getValidatorPkgName()
>>>
>>> # Determine the driver for this testcase.
>>> exec 'from '+driverPkg+' import HttpTestCase'
>>>
>>> # Determine the validator for this testcase.
>>> exec 'from '+validatorPkg+' import Validator'
>>>
>>> # imports
>>> from junit.textui import TestRunner
>>> from java.lang import *
>>> from java.io import *
>>> from java.util import *
>>> from org.apache.commons.httpclient import NameValuePair
>>>
>>>
>>> # definition of test class
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> class MaxQTest(HttpTestCase):
>>> def __init__(self, testName):
>>> HttpTestCase.__init__(self, testName)
>>>
>>> # Add setup code here. See Junit javadoc.
>>> def setUp(self):
>>> pass
>>>
>>> # Add tearDown code here. See Junit javadoc.
>>> def tearDown(self):
>>> pass
>>>
>>> # Runs the test
>>> def runTest(self):
>>> self.doTest()
>>>
>>> # Test script
>>> def doTest(self):
>>> self.currentParams = ArrayList()
>>> self.currentParams.a​dd(NameValuePair('''​scope''',
>>> '''projectAndSubs'''))
>>> self.currentParams.a​dd(NameValuePair('''​resultsPerPage''',
>>> '''40'''))
>>> self.currentParams.a​dd(NameValuePair('''​query''',
>>> '''ghjghj'''))
>>> self.currentParams.a​dd(NameValuePair('''​Button''', '''Go'''))
>>> print "Testing URL: %s" %
>>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>>> list = self.currentParams
>>> Validator.validateRequest(self, self.getMethod(), "get",
>>> self.currentUrl, self.currentParams)
>>> self.get(self.currentUrl, list)
>>> print "Response code: %s" % self.getMethod().get​StatusCode()
>>> self.assertEquals("Assert number 2 failed", 200,
>>> self.getMethod().get​StatusCode())
>>> Validator.validateRe​sponse(self, self.getMethod(),
>>> self.currentUrl, self.currentParams)
>>>
>>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>>
>>>
>>> ####################​####################​##
>>>
>>> # Code to load and run the test
>>> if __name__ == 'main':
>>> logger = Config.getTestLogger()
>>> test = MaxQTest("MaxQTest")
>>> test.Run()
>>>
>>> SIXTY SIX LINES!!! When it's simple, Jython/Python is a language
>>> that inexperienced programmers can understand and get started on
>>> quickly. Presumably this is why it was chosen for MaxQ. But this?
>>> Can you imagine the reaction of most people when they download MaxQ
>>> and it produces this stuff? They go elsewhere.
>>>
>>> I propose these changes:
>>>
>>> - We switch from encoding parameters in an ArrayList of
>>> NameValuePairs to a Jython array of tuples. This will make the
>>> scripts more concise and will stop them being tied to HttpClient.
>>> e.g.
>>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>>> ('''resultsPerPage''', '''40'''),
>>> ('''query''', '''ghjghj'''),
>>> ('''Button''', '''Go''')]
>>>
>>> - We remove the empty setUp() and tearDown() functions. They are
>>> documented and so need not be included in every script. We can also
>>> include some sample scripts showing how to use them.
>>>
>>> - We get rid of doTest() and put recorded code in runTest(). It is
>>> trivial for the user to break the code into multiple functions if
>>> this is what she desires. Again, examples will assist.
>>>
>>> - We stop using esoteric syntax to allow Config to override the
>>> class for HttpTestCase and Validator. No new MaxQ user will want to
>>> do this, rather they will wonder: "What they hell is this? This
>>> doesn't appear in the Python tutorial!" Now that the
>>> JythonGenerator code is much simpler, it would be easy to derive a
>>> new class from JythonGenerator to change these base classes for
>>> whichever sophisticated users wish it.
>>>
>>> - We stop loading a logger. We do not want people to use a logger
>>> because it will stop output going to stdout/stderr and therefore it
>>> will not appear in MaxQ's run window. "print" is obviously easier
>>> for a user to understand, and that's what the script uses anyway.
>>>
>>> We would end up with a script like this:
>>>
>>> # Generated by MaxQ
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> from com.bitmechanic.maxq import Validator
>>>
>>> class MaxQTest(HttpTestCase):
>>> def runTest(self):
>>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>>> ('''resultsPerPage''', '''40'''),
>>> ('''query''', '''ghjghj'''),
>>> ('''Button''', '''Go''')]
>>> print "Testing URL: %s" %
>>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>>> Validator.validateRequest(self, self.getMethod(), "get",
>>> self.currentUrl, self.currentParams)
>>> self.get(self.currentUrl, self.currentParams)
>>> print "Response code: %s" % self.getMethod().get​StatusCode()
>>> self.assertEquals("Assert number 2 failed", 200,
>>> self.getMethod().get​StatusCode())
>>> Validator.validateRe​sponse(self, self.getMethod(),
>>> self.currentUrl, self.currentParams)
>>>
>>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>>
>>>
>>> # Code to load and run the test
>>> if __name__ == 'main':
>>> test = MaxQTest("MaxQTest")
>>> test.Run()
>>>
>>>
>>> Oliver
>>>
>>>
>>> --------------------​--------------------​--------------------​---------
>>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>>
>>>
>> ---
>> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374
>> 7426
>> Author of "Java Testing and Design: From Unit Tests to Automated Web
>> Tests"
>> from Prentice Hall, details at http://thebook.pushtotest.com
>>
>>
>> --------------------​--------------------​--------------------​---------
>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>
>>
> ---
> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
> Author of "Java Testing and Design: From Unit Tests to Automated Web
> Tests"
> from Prentice Hall, details at http://thebook.pushtotest.com
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org




--------------------​-------------
Do You Yahoo!?
150万曲MP3疯狂搜,带您闯入音乐殿堂
美女明星应有尽有,搜遍美图、艳图和酷图
1G就是1000兆,雅虎电邮自助扩容!
Attachments

Re: [maxq-dev] Obese scripts

Author hdara at primavera dot com
Full name hdara at primavera dot com
Date 2004-09-27 22:47:15 PDT
Message Oliver Bock <oliver at g7 dot org> wrote on 09/27/2004 09:00:49 PM:

> > - I added the switchable driver class (and validator class), such that

> > the
> > same script can be dynamically run by a different driver. The idea is
> > that
> > when we extend the usefulness of the tool such that even QA is tempted

> > to
> > use it, we want them to have an option to visualize the execution of
> > the
> > script. If we can design a driver that can redirect the calls to a
> > browser
> > some how, then the same script can be rerun with a different driver to

> > see
> > the execution in action. If you just switch the generator during the
> > record time, you don't get the same value. If you can come up with a
> > more
> > elegant solution to this problem, I have no problem removing it.
> > - I am OK with the rest of the changes. Leaving the logger might not
> > be a
> > bad idea.
>
> Are replacement classes that take advantage of these features available?
>

Nope, they are not available right now, but would love to work on them if
time permits.

Hari

--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

[maxq-dev] Regular expressions in Jython?

Author fcohen
Full name Frank Cohen
Date 2004-09-27 22:40:49 PDT
Message Hi Oliver: About your comment:

> If anyone wants to match response codes with a regular expressions
> then they can just change:
>
> self.assertEquals("Assert number 2 failed", 200,
> self.getMethod().get​StatusCode())
>
> to (say)
>
> self.assert(re.match​(r'20[012]', self.getMethod().get​StatusCode()))
>
> Is this sufficient? How would you see that the code would look?


I am unable to get jUnit's TestCase.assert to handle a Jython re
expression. When I process:

assert( re.match( "He", "Hello" ) )

I get an exception: 1st arg cannot be coerced to boolean.

Any ideas why?

-Frank


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author fcohen
Full name Frank Cohen
Date 2004-09-27 22:14:42 PDT
Message On Sep 27, 2004, at 8:59 PM, Oliver Bock wrote:

> Frank,
>
> Sorry, now I see that you have provided an example. I like that your
> logging is self-contained. Perhaps we should include a self.log()
> function in the HttpBaseTest that will output just like "print"
> normally, but which can be turned off by calling another function,
> like self.suppressLog() or self.setLogLevel(). This way it won't get
> in the way of normal users, but is available for people who want it.

Yeh, I'm at the same quandary. I expanded the log function to allow
users to choose where they are logging-to: file, console, jdbc
datasource. Now it's even more complex and I'm not sure if it belongs
in a baseclass?


>
> Your script looks reasonable as a JUnit test, except that it is not
> derived from TestCase. I have not used MaxQ scripts as real JUnit
> tests, but since they are derived form TestCase it should just be a
> matter of compiling them into Java classes (jythonc) and including
> maxq.jar in the classpath.
>

Yes, it's the calling package that will want to know that the script
implement's TestCase's methods. So it's fine not to subclass TestCase
since it implements all of its methods.


>
> Oliver
>
> --
> On 28/09/2004, at 08:21, Frank Cohen wrote:
>
>> One other thing... In an earlier email I proposed using a log-level
>> variable to determine which information get printed to the console as
>> the test script operations. Did you consider this? Below is my sample
>> script that incorporates the log-level.
>>
>> -Frank
>>
>>
>> A few TestMaker users have asked me about support of jUnit. It seems
>> to me that the TestMaker recorder should be outputing tests that
>> conform to a jUnit TestCase. Below is a script that accomplishes
>> this. I am seeking your input, feedback and criticism of this script
>> before I make any changes to the recorder.
>>
>> '''
>> Agent name: mytest.py
>> Created by: TestMaker Agent Recorder
>> Created on:
>>
>> Purpose:
>> Calculates a Transaction-Per-Second index for a
>> Web-enabled HTTP application
>>
>> For details on TestMaker see http://www.pushtotest.com
>> '''
>>
>> from com.pushtotest.tool.​protocolhandler import ProtocolHandler,
>> HTTPProtocol
>> from com.pushtotest.tool.response import Response
>> from java.util import Date
>> from java.lang import Exception
>> from junit.framework import TestCase
>>
>> '''
>> The test class implements the steps recorded while watching a user
>> operate the application using a browser.
>> '''
>>
>> class mytest:
>>
>> def __init__( self, loglvl = 0 ):
>> ''' Initialization '''
>> self.loglevel = loglvl
>> if self.loglevel > 0:
>> print "mytest: Initialize"
>> self.steps = 0 # Counter of steps in transaction
>> self.http = ProtocolHandler.getP​rotocol("http")
>>
>> def setUp( self ):
>> ''' Add any needed set-up code here. '''
>> if self.loglevel > 0:
>> print "mytest: setUp"
>> pass
>>
>> def runTest( self ):
>> ''' Run the test '''
>> if self.loglevel > 0:
>> print "mytest: runTest"
>>
>> self.http.setUrl(
>> '''http://examples.push​totest.com/responder​/htmlresponder?
>> file=file1.html''' )
>> self.http.setType( HTTPProtocol.GET )
>> self.response = self.http.connect()
>> TestCase.assertEquals("Step 1 failed while trying to get: '"
>> + \
>> self.http.getURL("http"), 200,
>> self.response.getResponseCode() )
>> self.logit()
>>
>> self.http.setUrl(
>> '''http://examples.push​totest.com/responder​/htmlresponder''' )
>> self.http.setType( HTTPProtocol.POST )
>> self.body = ProtocolHandler.getBody( 'http' )
>> self.http.setBody( self.body )
>> self.body.addParamet​er('''firstname''', '''frank''')
>> self.body.addParamet​er('''lastname''', '''cohen''')
>> self.body.addParamet​er('''phone''', '''''')
>> self.body.addParamet​er('''account''', '''''')
>> self.body.addParamet​er('''amount''', '''''')
>> self.body.addParamet​er('''Transfer''', '''Transfer Funds''')
>> self.response = self.http.connect()
>> TestCase.assertEquals("Step 2 failed while trying to post: '"
>> + self.http.getURL("http"), 200, self.response.getResponseCode() )
>> self.logit()
>>
>> self.http.setUrl(
>> '''http://examples.push​totest.com/responder​/htmlresponder?
>> file=file3.html''' )
>> self.http.setType( HTTPProtocol.GET )
>> self.response = self.http.connect()
>> TestCase.assertEquals("Step 3 failed while trying to get: '"
>> + self.http.getURL("http"), 200, self.response.getResponseCode() )
>> self.logit()
>>
>> def logit( self ):
>> self.steps += 1
>> if self.loglevel > 1:
>> print "mytest: runTest, step", self.steps
>>
>> def tearDown( self ):
>> ''' Add any needed code to end the test here. '''
>>
>> def setLoglevel( self, loglv ):
>> self.loglevel = loglv
>> def getLogLevel( self ):
>> return self.loglevel
>> def getSteps( self ):
>> return self.steps
>>
>> # Code to instantiate and run the test
>>
>> if __name__ == 'main':
>>
>> print "===================​====================​================"
>> print " myTest.py: Functional test of a Web application "
>> print "===================​====================​================"
>> print "Test recorded by TestMaker from http://www.pushtotest.com"
>> print
>>
>> test = mytest( 2 ) # Value determines logging level, 0 =
>> off, 1 = Major messages only, 2 = Step details
>> test.runTest()
>>
>> print "Test executed", test.getSteps(), "steps."
>> print "Test completed."
>>
>>
>> I am open to your input, feedback and criticism of the above script.
>> Please let me know your thoughts. Thanks, in advance.
>>
>> -Frank
>>
>>
>>
>>
>> On Sep 27, 2004, at 3:08 PM, Frank Cohen wrote:
>>
>>> Nice summary of the problem and solution. I urge you to consider two
>>> changes to your proposal.
>>>
>>> 1) It's nice to see how many ideas are being passed around the MaxQ
>>> project. At this rate MaxQ is going to outgrow a one-size-fits-all
>>> format for the recorded scripts. For example, I really want the
>>> logging to stay. I propose adding a Preferences command that allows
>>> users to choose recording options.
>>>
>>> 2) Add a regex way to validate http response codes.
>>>
>>> -Frank
>>>
>>>
>>> On Sep 27, 2004, at 2:51 PM, Oliver Bock wrote:
>>>
>>>> I'm concerned that the scripts being generated by MaxQ are complex
>>>> and too large. While I mainly use my new "compact" scripts, I do
>>>> think that the old style is still useful for new users because they
>>>> can see what is happening. Its simplicity is also useful for
>>>> people who are only generating a few scripts and don't mind hand
>>>> editing them. Here is a script that submits a single form with
>>>> four parameters:
>>>>
>>>> # Generated by MaxQ
>>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>>> from sys import *
>>>>
>>>> from com.bitmechanic.maxq import Config
>>>> global driverPkg, validatorPkg, logger
>>>> if __name__ == 'main':
>>>> driverPkg = Config.getDriverPkgName()
>>>> validatorPkg = Config.getValidatorPkgName()
>>>>
>>>> # Determine the driver for this testcase.
>>>> exec 'from '+driverPkg+' import HttpTestCase'
>>>>
>>>> # Determine the validator for this testcase.
>>>> exec 'from '+validatorPkg+' import Validator'
>>>>
>>>> # imports
>>>> from junit.textui import TestRunner
>>>> from java.lang import *
>>>> from java.io import *
>>>> from java.util import *
>>>> from org.apache.commons.httpclient import NameValuePair
>>>>
>>>>
>>>> # definition of test class
>>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>>> class MaxQTest(HttpTestCase):
>>>> def __init__(self, testName):
>>>> HttpTestCase.__init__(self, testName)
>>>>
>>>> # Add setup code here. See Junit javadoc.
>>>> def setUp(self):
>>>> pass
>>>>
>>>> # Add tearDown code here. See Junit javadoc.
>>>> def tearDown(self):
>>>> pass
>>>>
>>>> # Runs the test
>>>> def runTest(self):
>>>> self.doTest()
>>>>
>>>> # Test script
>>>> def doTest(self):
>>>> self.currentParams = ArrayList()
>>>> self.currentParams.a​dd(NameValuePair('''​scope''',
>>>> '''projectAndSubs'''))
>>>> self.currentParams.a​dd(NameValuePair('''​resultsPerPage''',
>>>> '''40'''))
>>>> self.currentParams.a​dd(NameValuePair('''​query''',
>>>> '''ghjghj'''))
>>>> self.currentParams.a​dd(NameValuePair('''​Button''',
>>>> '''Go'''))
>>>> print "Testing URL: %s" %
>>>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>>>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>>>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>>>> list = self.currentParams
>>>> Validator.validateRequest(self, self.getMethod(), "get",
>>>> self.currentUrl, self.currentParams)
>>>> self.get(self.currentUrl, list)
>>>> print "Response code: %s" % self.getMethod().get​StatusCode()
>>>> self.assertEquals("Assert number 2 failed", 200,
>>>> self.getMethod().get​StatusCode())
>>>> Validator.validateRe​sponse(self, self.getMethod(),
>>>> self.currentUrl, self.currentParams)
>>>>
>>>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>>>
>>>>
>>>> ####################​####################​##
>>>>
>>>> # Code to load and run the test
>>>> if __name__ == 'main':
>>>> logger = Config.getTestLogger()
>>>> test = MaxQTest("MaxQTest")
>>>> test.Run()
>>>>
>>>> SIXTY SIX LINES!!! When it's simple, Jython/Python is a language
>>>> that inexperienced programmers can understand and get started on
>>>> quickly. Presumably this is why it was chosen for MaxQ. But this?
>>>> Can you imagine the reaction of most people when they download
>>>> MaxQ and it produces this stuff? They go elsewhere.
>>>>
>>>> I propose these changes:
>>>>
>>>> - We switch from encoding parameters in an ArrayList of
>>>> NameValuePairs to a Jython array of tuples. This will make the
>>>> scripts more concise and will stop them being tied to HttpClient.
>>>> e.g.
>>>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>>>> ('''resultsPerPage''', '''40'''),
>>>> ('''query''', '''ghjghj'''),
>>>> ('''Button''', '''Go''')]
>>>>
>>>> - We remove the empty setUp() and tearDown() functions. They are
>>>> documented and so need not be included in every script. We can
>>>> also include some sample scripts showing how to use them.
>>>>
>>>> - We get rid of doTest() and put recorded code in runTest(). It is
>>>> trivial for the user to break the code into multiple functions if
>>>> this is what she desires. Again, examples will assist.
>>>>
>>>> - We stop using esoteric syntax to allow Config to override the
>>>> class for HttpTestCase and Validator. No new MaxQ user will want
>>>> to do this, rather they will wonder: "What they hell is this? This
>>>> doesn't appear in the Python tutorial!" Now that the
>>>> JythonGenerator code is much simpler, it would be easy to derive a
>>>> new class from JythonGenerator to change these base classes for
>>>> whichever sophisticated users wish it.
>>>>
>>>> - We stop loading a logger. We do not want people to use a logger
>>>> because it will stop output going to stdout/stderr and therefore it
>>>> will not appear in MaxQ's run window. "print" is obviously easier
>>>> for a user to understand, and that's what the script uses anyway.
>>>>
>>>> We would end up with a script like this:
>>>>
>>>> # Generated by MaxQ
>>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>>> from com.bitmechanic.maxq import Validator
>>>>
>>>> class MaxQTest(HttpTestCase):
>>>> def runTest(self):
>>>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>>>> ('''resultsPerPage''', '''40'''),
>>>> ('''query''', '''ghjghj'''),
>>>> ('''Button''', '''Go''')]
>>>> print "Testing URL: %s" %
>>>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>>>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>>>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>>>> Validator.validateRequest(self, self.getMethod(), "get",
>>>> self.currentUrl, self.currentParams)
>>>> self.get(self.currentUrl, self.currentParams)
>>>> print "Response code: %s" % self.getMethod().get​StatusCode()
>>>> self.assertEquals("Assert number 2 failed", 200,
>>>> self.getMethod().get​StatusCode())
>>>> Validator.validateRe​sponse(self, self.getMethod(),
>>>> self.currentUrl, self.currentParams)
>>>>
>>>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>>>
>>>>
>>>> # Code to load and run the test
>>>> if __name__ == 'main':
>>>> test = MaxQTest("MaxQTest")
>>>> test.Run()
>>>>
>>>>
>>>> Oliver
>>>>
>>>>
>>>> --------------------​--------------------​--------------------​--------
>>>> -
>>>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>>>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>>>
>>>>
>>> ---
>>> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374
>>> 7426
>>> Author of "Java Testing and Design: From Unit Tests to Automated Web
>>> Tests"
>>> from Prentice Hall, details at http://thebook.pushtotest.com
>>>
>>>
>>> --------------------​--------------------​--------------------​---------
>>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>>
>>>
>> ---
>> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374
>> 7426
>> Author of "Java Testing and Design: From Unit Tests to Automated Web
>> Tests"
>> from Prentice Hall, details at http://thebook.pushtotest.com
>>
>>
>> --------------------​--------------------​--------------------​---------
>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>
>>
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>
---
Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
Author of "Java Testing and Design: From Unit Tests to Automated Web
Tests"
from Prentice Hall, details at http://thebook.pushtotest.com


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author fcohen
Full name Frank Cohen
Date 2004-09-27 22:10:32 PDT
Message On Sep 27, 2004, at 8:54 PM, Oliver Bock wrote:

> On 28/09/2004, at 08:08, Frank Cohen wrote:
>> ...At this rate MaxQ is going to outgrow a one-size-fits-all format
>> for the recorded scripts. For example, I really want the logging to
>> stay. I propose adding a Preferences command that allows users to
>> choose recording options.
>
> MaxQ now supports multiple script generators. Hopefully the ability
> to easily create new generators will take some of the pressure off the
> original generator class. (Each installed generator appears in the
> File -> New menu so they are easy to use.)

I see your point. I was working on the assumption that some MaxQ users
aren't Java coders and won't know that the script generator classes
exist. No prob.


>
> Both you and Hari like the logging, but I think distinguish between
> two types of logging:
>
> 1. Logging from MaxQ. MaxQ Java code uses logging to provide
> information about what it is doing. These messages are useful while
> debugging MaxQ and sometimes for understanding strange behaviour in
> your scripts. This logging is now well supported by MaxQ and can be
> turned on or suppressed using the -d and -q command line flags.
>
> 2. Logging from a script. Users need this to see what is going
> between their script and the web site being tested. Currently scripts
> report their own activities using the Jython "print" statement, but I
> guess you and Hari would prefer it was done by some other mechanism?
> Whichever mechanism that is, its output should appear in the Run
> window, not on Java's stderr.

> Could you or Hari provide some examples of the sort of logging you
> want to do?
>

I think you have it right. I'm looking for helpers that don't require
me to use a debugger when I'm debugging a script. My assertion is that
a log level variable should be in the debugging information such that
at the lowest level there is no debugging information output to the
console and that as you move the level up you get more detail, with the
highest level of details being the HTTP response from the host.

Below is my latest work on upgrading the TestMaker recorder. This is
pretty close to what I'm going to propose as TestMaker's standard
recording output. (I'm sorry for the flood but here goes...)


'''
Agent name: mytest.py
Created by: TestMaker New Agent Wizard Recorder
Created on: September 27, 2004 at 7:49 p.m.

Note:
Turn this functional test into a scalability and load
test using XSTest found in testmaker_home/XSTest

Turn this functional test into a Quality of Service monitor
using PushToTest Service Monitor System (SMS) found at
http://www.pushtotes​t.com/Services/slmso​lution.html

For details on TestMaker see http://www.pushtotest.com
'''

from com.pushtotest.tool.​protocolhandler import ProtocolHandler
from com.pushtotest.tool.response import Response
from com.pushtotest.tool.logger import simplelogger
from com.pushtotest.tool import TestCase
from java.util import Date
import sys
from java.lang import Exception

class mytest:
     '''
     Conducts a functional test of a Web-enabled HTTP application
     '''

     def __init__( self, debuglevel = 0, logto="console",
follow_redirects=1, \
     stopon="none", httperror="200x", logpath="log.txt" ):
         ''' Initialize the test class '''

         self.logto = logto # Log destination (console,
chart, file)
         self.loglevel = loglvl # How much debug information to
display
                                     # 0 = none, 1 = info, 2 = details,
3 = everything
         self.logger = None # Simplelogger object for logging
to files
         self.logpath = logpath # Log file name, if applicable
         self.log( 1, "mytest: Initialize" )

         self.follow_redirects = follow_redirects # Follow HTTP 302
responses
         self.steps = 0 # Counter of steps in transaction

         # RegEx list of HTTP response codes that are NOT errors
         self.codes = [ 20., 300, 301, 302, 303, 304, 307, 401, 403,
404, 408, 41. ]

         self.http = ProtocolHandler.getP​rotocol("http")

     def setUp( self ):
         ''' Add any needed set-up code here. '''
         self.log( 1, "mytest: setUp" )
         pass

     def runTest( self ):
         ''' Run the test '''
         self.log( 1, "mytest: runTest" )

         self.http.setUrl(
'''http://examples.push​totest.com/responder​/htmlresponder?
file=file1.html''' )
         self.http.setType( HTTPProtocol.GET )
         self.response = self.http.connect()
         self.log( 2, self.response )
         TestCase.assertEqualsRegex( self.errMessage(), self.codes,
self.response.getResponseCode() )
         self.steps = self.steps + 1
         self.log( 1, "Finished step " + str( self.steps ) )

         self.http.setUrl(
'''http://examples.push​totest.com/responder​/htmlresponder''' )
         self.http.setType( HTTPProtocol.POST )
         self.body = ProtocolHandler.getBody( 'http' )
         self.http.setBody( self.body )
         self.body.addParamet​er('''firstname''', '''frank''')
         self.body.addParamet​er('''lastname''', '''cohen''')
         self.body.addParamet​er('''phone''', '''''')
         self.body.addParamet​er('''account''', '''''')
         self.body.addParamet​er('''amount''', '''''')
         self.body.addParamet​er('''Transfer''', '''Transfer Funds''')
         self.response = self.http.connect()
         self.log( 2, self.response )
         TestCase.assertEqualsRegex( self.errMessage(), self.codes,
self.response.getResponseCode() )
         self.steps = self.steps + 1
         self.log( 1, "Finished step " + str( self.steps ) )

         self.http.setUrl(
'''http://examples.push​totest.com/responder​/htmlresponder?
file=file3.html''' )
         self.http.setType( HTTPProtocol.GET )
         self.response = self.http.connect()
         self.log( 2, self.response )
         TestCase.assertEqualsRegex( self.errMessage(), self.codes,
self.response.getResponseCode() )
         self.steps = self.steps + 1
         self.log( 1, "Finished step " + str( self.steps ) )

     def tearDown( self ):
         ''' Add any needed code to end the test here. '''
         self.log( 1, "mytest: setUp" )
         pass

     ''' Supporting methods for the test class '''

     def errMessage( self ):
         return "Step " + str( self.steps ) + \
         " got response " + str( self.response.getResponseCode() ) + \
         " when requesting: '" + self.http.getURL()

     def log( self, lvl, message ):
         ''' Log results and debug information '''

         if ( self.logger == None ) and ( self.logto.count("file") ):
             try:
                 self.logger = simplelogger.getInstance()
                 self.logger.setFileName( self.logpath )
             except java.lang.Exception, ex:
                 print "Could not start logging. Shutting down. ",ex
                 sys.exit(1)

         if self.loglevel >= lvl:

             if logto.count("console"): # Display the message on the
console
                 print message

             if logto.count("file"): # Save the message to a file
                 self.log.log( entry )

             if logto.count("sql"): # Insert the message into a
database table
                 '''
                 Add the JDBC commands to insert the message into a
database
                 table. These commands often look like this:

                 from java.sql import *
                  
Class.forName("COM.i​bm.db2.jdbc.app.DB2D​river").newInstance(​)
                 con = DriverManager.getConnection( 'jdbc:db2:sample',
'frank','loveslorette');
                 stmt = con.createStatement()
                 sql = "INSERT into logtable( message ) values " +
message + " )"
                 rs = stmt.executeQuery(sql)
                 rs.close()
                 stmt.close()
                 con.close()
                 '''

'''
Convenience amin method for running this test by itself
otherwise, plug this into XSTest to turn it into a scalability
and load test, and the Service Monitor System (SMS) for
a Quality of Service (QOS) monitor.
'''

if __name__ == 'main':
     print "===================​====================​================"
     print " myTest.py: Functional test of a Web application "
     print "===================​====================​================"
     print "Test created by TestMaker from http://www.pushtotest.com"
     print

     test = mytest()
     test.runTest()


>> 2) Add a regex way to validate http response codes.
>
> If anyone wants to match response codes with a regular expressions
> then they can just change:
>
> self.assertEquals("Assert number 2 failed", 200,
> self.getMethod().get​StatusCode())
>
> to (say)
>
> self.assert(re.match​(r'20[012]', self.getMethod().get​StatusCode()))
>
> Is this sufficient? How would you see that the code would look?
>

Nice! Works for me.


>
> Oliver
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>
---
Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
Author of "Java Testing and Design: From Unit Tests to Automated Web
Tests"
from Prentice Hall, details at http://thebook.pushtotest.com


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author oliverbock
Full name Oliver Bock
Date 2004-09-27 21:00:49 PDT
Message > - I added the switchable driver class (and validator class), such that
> the
> same script can be dynamically run by a different driver. The idea is
> that
> when we extend the usefulness of the tool such that even QA is tempted
> to
> use it, we want them to have an option to visualize the execution of
> the
> script. If we can design a driver that can redirect the calls to a
> browser
> some how, then the same script can be rerun with a different driver to
> see
> the execution in action. If you just switch the generator during the
> record time, you don't get the same value. If you can come up with a
> more
> elegant solution to this problem, I have no problem removing it.
> - I am OK with the rest of the changes. Leaving the logger might not
> be a
> bad idea.

Are replacement classes that take advantage of these features available?


    Oliver


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author oliverbock
Full name Oliver Bock
Date 2004-09-27 20:59:38 PDT
Message Frank,

Sorry, now I see that you have provided an example. I like that your
logging is self-contained. Perhaps we should include a self.log()
function in the HttpBaseTest that will output just like "print"
normally, but which can be turned off by calling another function, like
self.suppressLog() or self.setLogLevel(). This way it won't get in the
way of normal users, but is available for people who want it.

Your script looks reasonable as a JUnit test, except that it is not
derived from TestCase. I have not used MaxQ scripts as real JUnit
tests, but since they are derived form TestCase it should just be a
matter of compiling them into Java classes (jythonc) and including
maxq.jar in the classpath.


    Oliver

--
On 28/09/2004, at 08:21, Frank Cohen wrote:

> One other thing... In an earlier email I proposed using a log-level
> variable to determine which information get printed to the console as
> the test script operations. Did you consider this? Below is my sample
> script that incorporates the log-level.
>
> -Frank
>
>
> A few TestMaker users have asked me about support of jUnit. It seems
> to me that the TestMaker recorder should be outputing tests that
> conform to a jUnit TestCase. Below is a script that accomplishes this.
> I am seeking your input, feedback and criticism of this script before
> I make any changes to the recorder.
>
> '''
> Agent name: mytest.py
> Created by: TestMaker Agent Recorder
> Created on:
>
> Purpose:
> Calculates a Transaction-Per-Second index for a
> Web-enabled HTTP application
>
> For details on TestMaker see http://www.pushtotest.com
> '''
>
> from com.pushtotest.tool.​protocolhandler import ProtocolHandler,
> HTTPProtocol
> from com.pushtotest.tool.response import Response
> from java.util import Date
> from java.lang import Exception
> from junit.framework import TestCase
>
> '''
> The test class implements the steps recorded while watching a user
> operate the application using a browser.
> '''
>
> class mytest:
>
> def __init__( self, loglvl = 0 ):
> ''' Initialization '''
> self.loglevel = loglvl
> if self.loglevel > 0:
> print "mytest: Initialize"
> self.steps = 0 # Counter of steps in transaction
> self.http = ProtocolHandler.getP​rotocol("http")
>
> def setUp( self ):
> ''' Add any needed set-up code here. '''
> if self.loglevel > 0:
> print "mytest: setUp"
> pass
>
> def runTest( self ):
> ''' Run the test '''
> if self.loglevel > 0:
> print "mytest: runTest"
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder?
> file=file1.html''' )
> self.http.setType( HTTPProtocol.GET )
> self.response = self.http.connect()
> TestCase.assertEquals("Step 1 failed while trying to get: '" +
> \
> self.http.getURL("http"), 200, self.response.getResponseCode()
> )
> self.logit()
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder''' )
> self.http.setType( HTTPProtocol.POST )
> self.body = ProtocolHandler.getBody( 'http' )
> self.http.setBody( self.body )
> self.body.addParamet​er('''firstname''', '''frank''')
> self.body.addParamet​er('''lastname''', '''cohen''')
> self.body.addParamet​er('''phone''', '''''')
> self.body.addParamet​er('''account''', '''''')
> self.body.addParamet​er('''amount''', '''''')
> self.body.addParamet​er('''Transfer''', '''Transfer Funds''')
> self.response = self.http.connect()
> TestCase.assertEquals("Step 2 failed while trying to post: '"
> + self.http.getURL("http"), 200, self.response.getResponseCode() )
> self.logit()
>
> self.http.setUrl(
> '''http://examples.push​totest.com/responder​/htmlresponder?
> file=file3.html''' )
> self.http.setType( HTTPProtocol.GET )
> self.response = self.http.connect()
> TestCase.assertEquals("Step 3 failed while trying to get: '" +
> self.http.getURL("http"), 200, self.response.getResponseCode() )
> self.logit()
>
> def logit( self ):
> self.steps += 1
> if self.loglevel > 1:
> print "mytest: runTest, step", self.steps
>
> def tearDown( self ):
> ''' Add any needed code to end the test here. '''
>
> def setLoglevel( self, loglv ):
> self.loglevel = loglv
> def getLogLevel( self ):
> return self.loglevel
> def getSteps( self ):
> return self.steps
>
> # Code to instantiate and run the test
>
> if __name__ == 'main':
>
> print "===================​====================​================"
> print " myTest.py: Functional test of a Web application "
> print "===================​====================​================"
> print "Test recorded by TestMaker from http://www.pushtotest.com"
> print
>
> test = mytest( 2 ) # Value determines logging level, 0 =
> off, 1 = Major messages only, 2 = Step details
> test.runTest()
>
> print "Test executed", test.getSteps(), "steps."
> print "Test completed."
>
>
> I am open to your input, feedback and criticism of the above script.
> Please let me know your thoughts. Thanks, in advance.
>
> -Frank
>
>
>
>
> On Sep 27, 2004, at 3:08 PM, Frank Cohen wrote:
>
>> Nice summary of the problem and solution. I urge you to consider two
>> changes to your proposal.
>>
>> 1) It's nice to see how many ideas are being passed around the MaxQ
>> project. At this rate MaxQ is going to outgrow a one-size-fits-all
>> format for the recorded scripts. For example, I really want the
>> logging to stay. I propose adding a Preferences command that allows
>> users to choose recording options.
>>
>> 2) Add a regex way to validate http response codes.
>>
>> -Frank
>>
>>
>> On Sep 27, 2004, at 2:51 PM, Oliver Bock wrote:
>>
>>> I'm concerned that the scripts being generated by MaxQ are complex
>>> and too large. While I mainly use my new "compact" scripts, I do
>>> think that the old style is still useful for new users because they
>>> can see what is happening. Its simplicity is also useful for people
>>> who are only generating a few scripts and don't mind hand editing
>>> them. Here is a script that submits a single form with four
>>> parameters:
>>>
>>> # Generated by MaxQ
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> from sys import *
>>>
>>> from com.bitmechanic.maxq import Config
>>> global driverPkg, validatorPkg, logger
>>> if __name__ == 'main':
>>> driverPkg = Config.getDriverPkgName()
>>> validatorPkg = Config.getValidatorPkgName()
>>>
>>> # Determine the driver for this testcase.
>>> exec 'from '+driverPkg+' import HttpTestCase'
>>>
>>> # Determine the validator for this testcase.
>>> exec 'from '+validatorPkg+' import Validator'
>>>
>>> # imports
>>> from junit.textui import TestRunner
>>> from java.lang import *
>>> from java.io import *
>>> from java.util import *
>>> from org.apache.commons.httpclient import NameValuePair
>>>
>>>
>>> # definition of test class
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> class MaxQTest(HttpTestCase):
>>> def __init__(self, testName):
>>> HttpTestCase.__init__(self, testName)
>>>
>>> # Add setup code here. See Junit javadoc.
>>> def setUp(self):
>>> pass
>>>
>>> # Add tearDown code here. See Junit javadoc.
>>> def tearDown(self):
>>> pass
>>>
>>> # Runs the test
>>> def runTest(self):
>>> self.doTest()
>>>
>>> # Test script
>>> def doTest(self):
>>> self.currentParams = ArrayList()
>>> self.currentParams.a​dd(NameValuePair('''​scope''',
>>> '''projectAndSubs'''))
>>> self.currentParams.a​dd(NameValuePair('''​resultsPerPage''',
>>> '''40'''))
>>> self.currentParams.a​dd(NameValuePair('''​query''',
>>> '''ghjghj'''))
>>> self.currentParams.a​dd(NameValuePair('''​Button''', '''Go'''))
>>> print "Testing URL: %s" %
>>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>>> list = self.currentParams
>>> Validator.validateRequest(self, self.getMethod(), "get",
>>> self.currentUrl, self.currentParams)
>>> self.get(self.currentUrl, list)
>>> print "Response code: %s" % self.getMethod().get​StatusCode()
>>> self.assertEquals("Assert number 2 failed", 200,
>>> self.getMethod().get​StatusCode())
>>> Validator.validateRe​sponse(self, self.getMethod(),
>>> self.currentUrl, self.currentParams)
>>>
>>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>>
>>>
>>> ####################​####################​##
>>>
>>> # Code to load and run the test
>>> if __name__ == 'main':
>>> logger = Config.getTestLogger()
>>> test = MaxQTest("MaxQTest")
>>> test.Run()
>>>
>>> SIXTY SIX LINES!!! When it's simple, Jython/Python is a language
>>> that inexperienced programmers can understand and get started on
>>> quickly. Presumably this is why it was chosen for MaxQ. But this?
>>> Can you imagine the reaction of most people when they download MaxQ
>>> and it produces this stuff? They go elsewhere.
>>>
>>> I propose these changes:
>>>
>>> - We switch from encoding parameters in an ArrayList of
>>> NameValuePairs to a Jython array of tuples. This will make the
>>> scripts more concise and will stop them being tied to HttpClient.
>>> e.g.
>>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>>> ('''resultsPerPage''', '''40'''),
>>> ('''query''', '''ghjghj'''),
>>> ('''Button''', '''Go''')]
>>>
>>> - We remove the empty setUp() and tearDown() functions. They are
>>> documented and so need not be included in every script. We can also
>>> include some sample scripts showing how to use them.
>>>
>>> - We get rid of doTest() and put recorded code in runTest(). It is
>>> trivial for the user to break the code into multiple functions if
>>> this is what she desires. Again, examples will assist.
>>>
>>> - We stop using esoteric syntax to allow Config to override the
>>> class for HttpTestCase and Validator. No new MaxQ user will want to
>>> do this, rather they will wonder: "What they hell is this? This
>>> doesn't appear in the Python tutorial!" Now that the
>>> JythonGenerator code is much simpler, it would be easy to derive a
>>> new class from JythonGenerator to change these base classes for
>>> whichever sophisticated users wish it.
>>>
>>> - We stop loading a logger. We do not want people to use a logger
>>> because it will stop output going to stdout/stderr and therefore it
>>> will not appear in MaxQ's run window. "print" is obviously easier
>>> for a user to understand, and that's what the script uses anyway.
>>>
>>> We would end up with a script like this:
>>>
>>> # Generated by MaxQ
>>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>>> from com.bitmechanic.maxq import Validator
>>>
>>> class MaxQTest(HttpTestCase):
>>> def runTest(self):
>>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>>> ('''resultsPerPage''', '''40'''),
>>> ('''query''', '''ghjghj'''),
>>> ('''Button''', '''Go''')]
>>> print "Testing URL: %s" %
>>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>>> Validator.validateRequest(self, self.getMethod(), "get",
>>> self.currentUrl, self.currentParams)
>>> self.get(self.currentUrl, self.currentParams)
>>> print "Response code: %s" % self.getMethod().get​StatusCode()
>>> self.assertEquals("Assert number 2 failed", 200,
>>> self.getMethod().get​StatusCode())
>>> Validator.validateRe​sponse(self, self.getMethod(),
>>> self.currentUrl, self.currentParams)
>>>
>>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>>
>>>
>>> # Code to load and run the test
>>> if __name__ == 'main':
>>> test = MaxQTest("MaxQTest")
>>> test.Run()
>>>
>>>
>>> Oliver
>>>
>>>
>>> --------------------​--------------------​--------------------​---------
>>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>>
>>>
>> ---
>> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374
>> 7426
>> Author of "Java Testing and Design: From Unit Tests to Automated Web
>> Tests"
>> from Prentice Hall, details at http://thebook.pushtotest.com
>>
>>
>> --------------------​--------------------​--------------------​---------
>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>
>>
> ---
> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
> Author of "Java Testing and Design: From Unit Tests to Automated Web
> Tests"
> from Prentice Hall, details at http://thebook.pushtotest.com
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author oliverbock
Full name Oliver Bock
Date 2004-09-27 20:54:23 PDT
Message On 28/09/2004, at 08:08, Frank Cohen wrote:
> ...At this rate MaxQ is going to outgrow a one-size-fits-all format
> for the recorded scripts. For example, I really want the logging to
> stay. I propose adding a Preferences command that allows users to
> choose recording options.

MaxQ now supports multiple script generators. Hopefully the ability to
easily create new generators will take some of the pressure off the
original generator class. (Each installed generator appears in the
File -> New menu so they are easy to use.)

Both you and Hari like the logging, but I think distinguish between two
types of logging:

1. Logging from MaxQ. MaxQ Java code uses logging to provide
information about what it is doing. These messages are useful while
debugging MaxQ and sometimes for understanding strange behaviour in
your scripts. This logging is now well supported by MaxQ and can be
turned on or suppressed using the -d and -q command line flags.

2. Logging from a script. Users need this to see what is going between
their script and the web site being tested. Currently scripts report
their own activities using the Jython "print" statement, but I guess
you and Hari would prefer it was done by some other mechanism?
Whichever mechanism that is, its output should appear in the Run
window, not on Java's stderr.

Could you or Hari provide some examples of the sort of logging you want
to do?

> 2) Add a regex way to validate http response codes.

If anyone wants to match response codes with a regular expressions then
they can just change:

   self.assertEquals("Assert number 2 failed", 200,
self.getMethod().get​StatusCode())

to (say)

  self.assert(re.match​(r'20[012]', self.getMethod().get​StatusCode()))

Is this sufficient? How would you see that the code would look?


    Oliver


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author hdara at primavera dot com
Full name hdara at primavera dot com
Date 2004-09-27 15:26:32 PDT
Message Oliver Bock <oliver at g7 dot org> wrote on 09/27/2004 02:51:38 PM:

> I'm concerned that the scripts being generated by MaxQ are complex and
> too large. While I mainly use my new "compact" scripts, I do think
> that the old style is still useful for new users because they can see
> what is happening. Its simplicity is also useful for people who are
> only generating a few scripts and don't mind hand editing them. Here
> is a script that submits a single form with four parameters:
>
> # Generated by MaxQ [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
> from sys import *
>
> from com.bitmechanic.maxq import Config
> global driverPkg, validatorPkg, logger
> if __name__ == 'main':
> driverPkg = Config.getDriverPkgName()
> validatorPkg = Config.getValidatorPkgName()
>
> # Determine the driver for this testcase.
> exec 'from '+driverPkg+' import HttpTestCase'
>
> # Determine the validator for this testcase.
> exec 'from '+validatorPkg+' import Validator'
>
> # imports
> from junit.textui import TestRunner
> from java.lang import *
> from java.io import *
> from java.util import *
> from org.apache.commons.httpclient import NameValuePair
>
>
> # definition of test class
> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
> class MaxQTest(HttpTestCase):
> def __init__(self, testName):
> HttpTestCase.__init__(self, testName)
>
> # Add setup code here. See Junit javadoc.
> def setUp(self):
> pass
>
> # Add tearDown code here. See Junit javadoc.
> def tearDown(self):
> pass
>
> # Runs the test
> def runTest(self):
> self.doTest()
>
> # Test script
> def doTest(self):
> self.currentParams = ArrayList()
> self.currentParams.a​dd(NameValuePair('''​scope''',
> '''projectAndSubs'''))
> self.currentParams.a​dd(NameValuePair('''​resultsPerPage''',
> '''40'''))
> self.currentParams.a​dd(NameValuePair('''​query''',
'''ghjghj'''))
> self.currentParams.a​dd(NameValuePair('''​Button''', '''Go'''))
> print "Testing URL: %s" %
> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
> list = self.currentParams
> Validator.validateRequest(self, self.getMethod(), "get",
> self.currentUrl, self.currentParams)
> self.get(self.currentUrl, list)
> print "Response code: %s" % self.getMethod().get​StatusCode()
> self.assertEquals("Assert number 2 failed", 200,
> self.getMethod().get​StatusCode())
> Validator.validateRe​sponse(self, self.getMethod(),
> self.currentUrl, self.currentParams)
>
> # ^^^ Insert new recordings here. (Do not remove this line.)
>
>
> ####################​####################​##
>
> # Code to load and run the test
> if __name__ == 'main':
> logger = Config.getTestLogger()
> test = MaxQTest("MaxQTest")
> test.Run()
>
> SIXTY SIX LINES!!! When it's simple, Jython/Python is a language that
> inexperienced programmers can understand and get started on quickly.
> Presumably this is why it was chosen for MaxQ. But this? Can you
> imagine the reaction of most people when they download MaxQ and it
> produces this stuff? They go elsewhere.
>
> I propose these changes:
>
> - We switch from encoding parameters in an ArrayList of NameValuePairs
> to a Jython array of tuples. This will make the scripts more concise
> and will stop them being tied to HttpClient. e.g.
> self.currentParams = [('''scope''', '''projectAndSubs'''),
> ('''resultsPerPage''', '''40'''),
> ('''query''', '''ghjghj'''),
> ('''Button''', '''Go''')]
>
> - We remove the empty setUp() and tearDown() functions. They are
> documented and so need not be included in every script. We can also
> include some sample scripts showing how to use them.
>
> - We get rid of doTest() and put recorded code in runTest(). It is
> trivial for the user to break the code into multiple functions if this
> is what she desires. Again, examples will assist.
>
> - We stop using esoteric syntax to allow Config to override the class
> for HttpTestCase and Validator. No new MaxQ user will want to do this,
> rather they will wonder: "What they hell is this? This doesn't appear
> in the Python tutorial!" Now that the JythonGenerator code is much
> simpler, it would be easy to derive a new class from JythonGenerator to
> change these base classes for whichever sophisticated users wish it.
>
> - We stop loading a logger. We do not want people to use a logger
> because it will stop output going to stdout/stderr and therefore it
> will not appear in MaxQ's run window. "print" is obviously easier for
> a user to understand, and that's what the script uses anyway.
>
> We would end up with a script like this:
>
> # Generated by MaxQ [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
> from com.bitmechanic.maxq import Validator
>
> class MaxQTest(HttpTestCase):
> def runTest(self):
> self.currentParams = [('''scope''', '''projectAndSubs'''),
> ('''resultsPerPage''', '''40'''),
> ('''query''', '''ghjghj'''),
> ('''Button''', '''Go''')]
> print "Testing URL: %s" %
> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
> Validator.validateRequest(self, self.getMethod(), "get",
> self.currentUrl, self.currentParams)
> self.get(self.currentUrl, self.currentParams)
> print "Response code: %s" % self.getMethod().get​StatusCode()
> self.assertEquals("Assert number 2 failed", 200,
> self.getMethod().get​StatusCode())
> Validator.validateRe​sponse(self, self.getMethod(),
> self.currentUrl, self.currentParams)
>
> # ^^^ Insert new recordings here. (Do not remove this line.)
>
>
> # Code to load and run the test
> if __name__ == 'main':
> test = MaxQTest("MaxQTest")
> test.Run()
>
>
> Oliver
>

- You are probably right if a non-developer looks at the code (e.g., some
one from QA who doesn't know much about scripting languages).
- I added the switchable driver class (and validator class), such that the
same script can be dynamically run by a different driver. The idea is that
when we extend the usefulness of the tool such that even QA is tempted to
use it, we want them to have an option to visualize the execution of the
script. If we can design a driver that can redirect the calls to a browser
some how, then the same script can be rerun with a different driver to see
the execution in action. If you just switch the generator during the
record time, you don't get the same value. If you can come up with a more
elegant solution to this problem, I have no problem removing it.
- I am OK with the rest of the changes. Leaving the logger might not be a
bad idea.

Thanks,
Hari

--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author fcohen
Full name Frank Cohen
Date 2004-09-27 15:21:33 PDT
Message One other thing... In an earlier email I proposed using a log-level
variable to determine which information get printed to the console as
the test script operations. Did you consider this? Below is my sample
script that incorporates the log-level.

-Frank


A few TestMaker users have asked me about support of jUnit. It seems to
me that the TestMaker recorder should be outputing tests that conform
to a jUnit TestCase. Below is a script that accomplishes this. I am
seeking your input, feedback and criticism of this script before I make
any changes to the recorder.

'''
Agent name: mytest.py
Created by: TestMaker Agent Recorder
Created on:

Purpose:
Calculates a Transaction-Per-Second index for a
Web-enabled HTTP application

For details on TestMaker see http://www.pushtotest.com
'''

from com.pushtotest.tool.​protocolhandler import ProtocolHandler,
HTTPProtocol
from com.pushtotest.tool.response import Response
from java.util import Date
from java.lang import Exception
from junit.framework import TestCase

'''
The test class implements the steps recorded while watching a user
operate the application using a browser.
'''

class mytest:

     def __init__( self, loglvl = 0 ):
         ''' Initialization '''
         self.loglevel = loglvl
         if self.loglevel > 0:
             print "mytest: Initialize"
         self.steps = 0 # Counter of steps in transaction
         self.http = ProtocolHandler.getP​rotocol("http")

     def setUp( self ):
         ''' Add any needed set-up code here. '''
         if self.loglevel > 0:
             print "mytest: setUp"
         pass

     def runTest( self ):
         ''' Run the test '''
         if self.loglevel > 0:
             print "mytest: runTest"

         self.http.setUrl(
'''http://examples.push​totest.com/responder​/htmlresponder?
file=file1.html''' )
         self.http.setType( HTTPProtocol.GET )
         self.response = self.http.connect()
         TestCase.assertEquals("Step 1 failed while trying to get: '" + \
         self.http.getURL("http"), 200, self.response.getResponseCode() )
         self.logit()

         self.http.setUrl(
'''http://examples.push​totest.com/responder​/htmlresponder''' )
         self.http.setType( HTTPProtocol.POST )
         self.body = ProtocolHandler.getBody( 'http' )
         self.http.setBody( self.body )
         self.body.addParamet​er('''firstname''', '''frank''')
         self.body.addParamet​er('''lastname''', '''cohen''')
         self.body.addParamet​er('''phone''', '''''')
         self.body.addParamet​er('''account''', '''''')
         self.body.addParamet​er('''amount''', '''''')
         self.body.addParamet​er('''Transfer''', '''Transfer Funds''')
         self.response = self.http.connect()
         TestCase.assertEquals("Step 2 failed while trying to post: '" +
self.http.getURL("http"), 200, self.response.getResponseCode() )
         self.logit()

         self.http.setUrl(
'''http://examples.push​totest.com/responder​/htmlresponder?
file=file3.html''' )
         self.http.setType( HTTPProtocol.GET )
         self.response = self.http.connect()
         TestCase.assertEquals("Step 3 failed while trying to get: '" +
self.http.getURL("http"), 200, self.response.getResponseCode() )
         self.logit()

     def logit( self ):
         self.steps += 1
         if self.loglevel > 1:
             print "mytest: runTest, step", self.steps

     def tearDown( self ):
         ''' Add any needed code to end the test here. '''

     def setLoglevel( self, loglv ):
         self.loglevel = loglv
     def getLogLevel( self ):
         return self.loglevel
     def getSteps( self ):
         return self.steps

# Code to instantiate and run the test

if __name__ == 'main':

     print "===================​====================​================"
     print " myTest.py: Functional test of a Web application "
     print "===================​====================​================"
     print "Test recorded by TestMaker from http://www.pushtotest.com"
     print

     test = mytest( 2 ) # Value determines logging level, 0 =
off, 1 = Major messages only, 2 = Step details
     test.runTest()

     print "Test executed", test.getSteps(), "steps."
     print "Test completed."


I am open to your input, feedback and criticism of the above script.
Please let me know your thoughts. Thanks, in advance.

-Frank




On Sep 27, 2004, at 3:08 PM, Frank Cohen wrote:

> Nice summary of the problem and solution. I urge you to consider two
> changes to your proposal.
>
> 1) It's nice to see how many ideas are being passed around the MaxQ
> project. At this rate MaxQ is going to outgrow a one-size-fits-all
> format for the recorded scripts. For example, I really want the
> logging to stay. I propose adding a Preferences command that allows
> users to choose recording options.
>
> 2) Add a regex way to validate http response codes.
>
> -Frank
>
>
> On Sep 27, 2004, at 2:51 PM, Oliver Bock wrote:
>
>> I'm concerned that the scripts being generated by MaxQ are complex
>> and too large. While I mainly use my new "compact" scripts, I do
>> think that the old style is still useful for new users because they
>> can see what is happening. Its simplicity is also useful for people
>> who are only generating a few scripts and don't mind hand editing
>> them. Here is a script that submits a single form with four
>> parameters:
>>
>> # Generated by MaxQ
>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>> from sys import *
>>
>> from com.bitmechanic.maxq import Config
>> global driverPkg, validatorPkg, logger
>> if __name__ == 'main':
>> driverPkg = Config.getDriverPkgName()
>> validatorPkg = Config.getValidatorPkgName()
>>
>> # Determine the driver for this testcase.
>> exec 'from '+driverPkg+' import HttpTestCase'
>>
>> # Determine the validator for this testcase.
>> exec 'from '+validatorPkg+' import Validator'
>>
>> # imports
>> from junit.textui import TestRunner
>> from java.lang import *
>> from java.io import *
>> from java.util import *
>> from org.apache.commons.httpclient import NameValuePair
>>
>>
>> # definition of test class
>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>> class MaxQTest(HttpTestCase):
>> def __init__(self, testName):
>> HttpTestCase.__init__(self, testName)
>>
>> # Add setup code here. See Junit javadoc.
>> def setUp(self):
>> pass
>>
>> # Add tearDown code here. See Junit javadoc.
>> def tearDown(self):
>> pass
>>
>> # Runs the test
>> def runTest(self):
>> self.doTest()
>>
>> # Test script
>> def doTest(self):
>> self.currentParams = ArrayList()
>> self.currentParams.a​dd(NameValuePair('''​scope''',
>> '''projectAndSubs'''))
>> self.currentParams.a​dd(NameValuePair('''​resultsPerPage''',
>> '''40'''))
>> self.currentParams.a​dd(NameValuePair('''​query''',
>> '''ghjghj'''))
>> self.currentParams.a​dd(NameValuePair('''​Button''', '''Go'''))
>> print "Testing URL: %s" %
>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>> list = self.currentParams
>> Validator.validateRequest(self, self.getMethod(), "get",
>> self.currentUrl, self.currentParams)
>> self.get(self.currentUrl, list)
>> print "Response code: %s" % self.getMethod().get​StatusCode()
>> self.assertEquals("Assert number 2 failed", 200,
>> self.getMethod().get​StatusCode())
>> Validator.validateRe​sponse(self, self.getMethod(),
>> self.currentUrl, self.currentParams)
>>
>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>
>>
>> ####################​####################​##
>>
>> # Code to load and run the test
>> if __name__ == 'main':
>> logger = Config.getTestLogger()
>> test = MaxQTest("MaxQTest")
>> test.Run()
>>
>> SIXTY SIX LINES!!! When it's simple, Jython/Python is a language
>> that inexperienced programmers can understand and get started on
>> quickly. Presumably this is why it was chosen for MaxQ. But this?
>> Can you imagine the reaction of most people when they download MaxQ
>> and it produces this stuff? They go elsewhere.
>>
>> I propose these changes:
>>
>> - We switch from encoding parameters in an ArrayList of
>> NameValuePairs to a Jython array of tuples. This will make the
>> scripts more concise and will stop them being tied to HttpClient.
>> e.g.
>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>> ('''resultsPerPage''', '''40'''),
>> ('''query''', '''ghjghj'''),
>> ('''Button''', '''Go''')]
>>
>> - We remove the empty setUp() and tearDown() functions. They are
>> documented and so need not be included in every script. We can also
>> include some sample scripts showing how to use them.
>>
>> - We get rid of doTest() and put recorded code in runTest(). It is
>> trivial for the user to break the code into multiple functions if
>> this is what she desires. Again, examples will assist.
>>
>> - We stop using esoteric syntax to allow Config to override the class
>> for HttpTestCase and Validator. No new MaxQ user will want to do
>> this, rather they will wonder: "What they hell is this? This doesn't
>> appear in the Python tutorial!" Now that the JythonGenerator code is
>> much simpler, it would be easy to derive a new class from
>> JythonGenerator to change these base classes for whichever
>> sophisticated users wish it.
>>
>> - We stop loading a logger. We do not want people to use a logger
>> because it will stop output going to stdout/stderr and therefore it
>> will not appear in MaxQ's run window. "print" is obviously easier
>> for a user to understand, and that's what the script uses anyway.
>>
>> We would end up with a script like this:
>>
>> # Generated by MaxQ
>> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
>> from com.bitmechanic.maxq import Validator
>>
>> class MaxQTest(HttpTestCase):
>> def runTest(self):
>> self.currentParams = [('''scope''', '''projectAndSubs'''),
>> ('''resultsPerPage''', '''40'''),
>> ('''query''', '''ghjghj'''),
>> ('''Button''', '''Go''')]
>> print "Testing URL: %s" %
>> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
>> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
>> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
>> Validator.validateRequest(self, self.getMethod(), "get",
>> self.currentUrl, self.currentParams)
>> self.get(self.currentUrl, self.currentParams)
>> print "Response code: %s" % self.getMethod().get​StatusCode()
>> self.assertEquals("Assert number 2 failed", 200,
>> self.getMethod().get​StatusCode())
>> Validator.validateRe​sponse(self, self.getMethod(),
>> self.currentUrl, self.currentParams)
>>
>> # ^^^ Insert new recordings here. (Do not remove this line.)
>>
>>
>> # Code to load and run the test
>> if __name__ == 'main':
>> test = MaxQTest("MaxQTest")
>> test.Run()
>>
>>
>> Oliver
>>
>>
>> --------------------​--------------------​--------------------​---------
>> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
>> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>>
>>
> ---
> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
> Author of "Java Testing and Design: From Unit Tests to Automated Web
> Tests"
> from Prentice Hall, details at http://thebook.pushtotest.com
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>
---
Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
Author of "Java Testing and Design: From Unit Tests to Automated Web
Tests"
from Prentice Hall, details at http://thebook.pushtotest.com


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

Re: [maxq-dev] Obese scripts

Author fcohen
Full name Frank Cohen
Date 2004-09-27 15:08:32 PDT
Message Nice summary of the problem and solution. I urge you to consider two
changes to your proposal.

1) It's nice to see how many ideas are being passed around the MaxQ
project. At this rate MaxQ is going to outgrow a one-size-fits-all
format for the recorded scripts. For example, I really want the logging
to stay. I propose adding a Preferences command that allows users to
choose recording options.

2) Add a regex way to validate http response codes.

-Frank


On Sep 27, 2004, at 2:51 PM, Oliver Bock wrote:

> I'm concerned that the scripts being generated by MaxQ are complex and
> too large. While I mainly use my new "compact" scripts, I do think
> that the old style is still useful for new users because they can see
> what is happening. Its simplicity is also useful for people who are
> only generating a few scripts and don't mind hand editing them. Here
> is a script that submits a single form with four parameters:
>
> # Generated by MaxQ
> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
> from sys import *
>
> from com.bitmechanic.maxq import Config
> global driverPkg, validatorPkg, logger
> if __name__ == 'main':
> driverPkg = Config.getDriverPkgName()
> validatorPkg = Config.getValidatorPkgName()
>
> # Determine the driver for this testcase.
> exec 'from '+driverPkg+' import HttpTestCase'
>
> # Determine the validator for this testcase.
> exec 'from '+validatorPkg+' import Validator'
>
> # imports
> from junit.textui import TestRunner
> from java.lang import *
> from java.io import *
> from java.util import *
> from org.apache.commons.httpclient import NameValuePair
>
>
> # definition of test class
> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
> class MaxQTest(HttpTestCase):
> def __init__(self, testName):
> HttpTestCase.__init__(self, testName)
>
> # Add setup code here. See Junit javadoc.
> def setUp(self):
> pass
>
> # Add tearDown code here. See Junit javadoc.
> def tearDown(self):
> pass
>
> # Runs the test
> def runTest(self):
> self.doTest()
>
> # Test script
> def doTest(self):
> self.currentParams = ArrayList()
> self.currentParams.a​dd(NameValuePair('''​scope''',
> '''projectAndSubs'''))
> self.currentParams.a​dd(NameValuePair('''​resultsPerPage''',
> '''40'''))
> self.currentParams.a​dd(NameValuePair('''​query''',
> '''ghjghj'''))
> self.currentParams.a​dd(NameValuePair('''​Button''', '''Go'''))
> print "Testing URL: %s" %
> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
> list = self.currentParams
> Validator.validateRequest(self, self.getMethod(), "get",
> self.currentUrl, self.currentParams)
> self.get(self.currentUrl, list)
> print "Response code: %s" % self.getMethod().get​StatusCode()
> self.assertEquals("Assert number 2 failed", 200,
> self.getMethod().get​StatusCode())
> Validator.validateRe​sponse(self, self.getMethod(),
> self.currentUrl, self.currentParams)
>
> # ^^^ Insert new recordings here. (Do not remove this line.)
>
>
> ####################​####################​##
>
> # Code to load and run the test
> if __name__ == 'main':
> logger = Config.getTestLogger()
> test = MaxQTest("MaxQTest")
> test.Run()
>
> SIXTY SIX LINES!!! When it's simple, Jython/Python is a language that
> inexperienced programmers can understand and get started on quickly.
> Presumably this is why it was chosen for MaxQ. But this? Can you
> imagine the reaction of most people when they download MaxQ and it
> produces this stuff? They go elsewhere.
>
> I propose these changes:
>
> - We switch from encoding parameters in an ArrayList of NameValuePairs
> to a Jython array of tuples. This will make the scripts more concise
> and will stop them being tied to HttpClient. e.g.
> self.currentParams = [('''scope''', '''projectAndSubs'''),
> ('''resultsPerPage''', '''40'''),
> ('''query''', '''ghjghj'''),
> ('''Button''', '''Go''')]
>
> - We remove the empty setUp() and tearDown() functions. They are
> documented and so need not be included in every script. We can also
> include some sample scripts showing how to use them.
>
> - We get rid of doTest() and put recorded code in runTest(). It is
> trivial for the user to break the code into multiple functions if this
> is what she desires. Again, examples will assist.
>
> - We stop using esoteric syntax to allow Config to override the class
> for HttpTestCase and Validator. No new MaxQ user will want to do
> this, rather they will wonder: "What they hell is this? This doesn't
> appear in the Python tutorial!" Now that the JythonGenerator code is
> much simpler, it would be easy to derive a new class from
> JythonGenerator to change these base classes for whichever
> sophisticated users wish it.
>
> - We stop loading a logger. We do not want people to use a logger
> because it will stop output going to stdout/stderr and therefore it
> will not appear in MaxQ's run window. "print" is obviously easier for
> a user to understand, and that's what the script uses anyway.
>
> We would end up with a script like this:
>
> # Generated by MaxQ
> [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
> from com.bitmechanic.maxq import Validator
>
> class MaxQTest(HttpTestCase):
> def runTest(self):
> self.currentParams = [('''scope''', '''projectAndSubs'''),
> ('''resultsPerPage''', '''40'''),
> ('''query''', '''ghjghj'''),
> ('''Button''', '''Go''')]
> print "Testing URL: %s" %
> self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
> scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
> self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
> Validator.validateRequest(self, self.getMethod(), "get",
> self.currentUrl, self.currentParams)
> self.get(self.currentUrl, self.currentParams)
> print "Response code: %s" % self.getMethod().get​StatusCode()
> self.assertEquals("Assert number 2 failed", 200,
> self.getMethod().get​StatusCode())
> Validator.validateRe​sponse(self, self.getMethod(),
> self.currentUrl, self.currentParams)
>
> # ^^^ Insert new recordings here. (Do not remove this line.)
>
>
> # Code to load and run the test
> if __name__ == 'main':
> test = MaxQTest("MaxQTest")
> test.Run()
>
>
> Oliver
>
>
> --------------------​--------------------​--------------------​---------
> To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
> For additional commands, e-mail: dev-help at maxq dot tigris dot org
>
>
---
Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
Author of "Java Testing and Design: From Unit Tests to Automated Web
Tests"
from Prentice Hall, details at http://thebook.pushtotest.com


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org

[maxq-dev] Obese scripts

Author oliverbock
Full name Oliver Bock
Date 2004-09-27 14:51:38 PDT
Message I'm concerned that the scripts being generated by MaxQ are complex and
too large. While I mainly use my new "compact" scripts, I do think
that the old style is still useful for new users because they can see
what is happening. Its simplicity is also useful for people who are
only generating a few scripts and don't mind hand editing them. Here
is a script that submits a single form with four parameters:

# Generated by MaxQ [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
from sys import *

from com.bitmechanic.maxq import Config
global driverPkg, validatorPkg, logger
if __name__ == 'main':
     driverPkg = Config.getDriverPkgName()
     validatorPkg = Config.getValidatorPkgName()

# Determine the driver for this testcase.
exec 'from '+driverPkg+' import HttpTestCase'

# Determine the validator for this testcase.
exec 'from '+validatorPkg+' import Validator'

# imports
from junit.textui import TestRunner
from java.lang import *
from java.io import *
from java.util import *
from org.apache.commons.httpclient import NameValuePair


# definition of test class
[com.bitmechanic.max​q.generator.JythonCo​deGenerator]
class MaxQTest(HttpTestCase):
     def __init__(self, testName):
         HttpTestCase.__init__(self, testName)

     # Add setup code here. See Junit javadoc.
     def setUp(self):
         pass

     # Add tearDown code here. See Junit javadoc.
     def tearDown(self):
         pass

     # Runs the test
     def runTest(self):
         self.doTest()

     # Test script
     def doTest(self):
         self.currentParams = ArrayList()
         self.currentParams.a​dd(NameValuePair('''​scope''',
'''projectAndSubs'''))
         self.currentParams.a​dd(NameValuePair('''​resultsPerPage''',
'''40'''))
         self.currentParams.a​dd(NameValuePair('''​query''', '''ghjghj'''))
         self.currentParams.a​dd(NameValuePair('''​Button''', '''Go'''))
         print "Testing URL: %s" %
self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
         self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
         list = self.currentParams
         Validator.validateRequest(self, self.getMethod(), "get",
self.currentUrl, self.currentParams)
         self.get(self.currentUrl, list)
         print "Response code: %s" % self.getMethod().get​StatusCode()
         self.assertEquals("Assert number 2 failed", 200,
self.getMethod().get​StatusCode())
         Validator.validateRe​sponse(self, self.getMethod(),
self.currentUrl, self.currentParams)

     # ^^^ Insert new recordings here. (Do not remove this line.)


####################​####################​##

# Code to load and run the test
if __name__ == 'main':
     logger = Config.getTestLogger()
     test = MaxQTest("MaxQTest")
     test.Run()

SIXTY SIX LINES!!! When it's simple, Jython/Python is a language that
inexperienced programmers can understand and get started on quickly.
Presumably this is why it was chosen for MaxQ. But this? Can you
imagine the reaction of most people when they download MaxQ and it
produces this stuff? They go elsewhere.

I propose these changes:

- We switch from encoding parameters in an ArrayList of NameValuePairs
to a Jython array of tuples. This will make the scripts more concise
and will stop them being tied to HttpClient. e.g.
         self.currentParams = [('''scope''', '''projectAndSubs'''),
                    ('''resultsPerPage''', '''40'''),
                    ('''query''', '''ghjghj'''),
                    ('''Button''', '''Go''')]

- We remove the empty setUp() and tearDown() functions. They are
documented and so need not be included in every script. We can also
include some sample scripts showing how to use them.

- We get rid of doTest() and put recorded code in runTest(). It is
trivial for the user to break the code into multiple functions if this
is what she desires. Again, examples will assist.

- We stop using esoteric syntax to allow Config to override the class
for HttpTestCase and Validator. No new MaxQ user will want to do this,
rather they will wonder: "What they hell is this? This doesn't appear
in the Python tutorial!" Now that the JythonGenerator code is much
simpler, it would be easy to derive a new class from JythonGenerator to
change these base classes for whichever sophisticated users wish it.

- We stop loading a logger. We do not want people to use a logger
because it will stop output going to stdout/stderr and therefore it
will not appear in MaxQ's run window. "print" is obviously easier for
a user to understand, and that's what the script uses anyway.

We would end up with a script like this:

# Generated by MaxQ [com.bitmechanic.max​q.generator.JythonCo​deGenerator]
from com.bitmechanic.maxq import Validator

class MaxQTest(HttpTestCase):
     def runTest(self):
         self.currentParams = [('''scope''', '''projectAndSubs'''),
                    ('''resultsPerPage''', '''40'''),
                    ('''query''', '''ghjghj'''),
                    ('''Button''', '''Go''')]
         print "Testing URL: %s" %
self.replaceURL('''http://maxq.tigris.o​rg/servlets/Search?
scope=projectAndSubs​&resultsPerPage=​40&query=ghjghj​&Button=Go''')
         self.currentUrl = "http://maxq.tigris.o​rg/servlets/Search"
         Validator.validateRequest(self, self.getMethod(), "get",
self.currentUrl, self.currentParams)
         self.get(self.currentUrl, self.currentParams)
         print "Response code: %s" % self.getMethod().get​StatusCode()
         self.assertEquals("Assert number 2 failed", 200,
self.getMethod().get​StatusCode())
         Validator.validateRe​sponse(self, self.getMethod(),
self.currentUrl, self.currentParams)

     # ^^^ Insert new recordings here. (Do not remove this line.)


# Code to load and run the test
if __name__ == 'main':
     test = MaxQTest("MaxQTest")
     test.Run()


    Oliver


--------------------​--------------------​--------------------​---------
To unsubscribe, e-mail: dev-unsubscribe at maxq dot tigris dot org
For additional commands, e-mail: dev-help at maxq dot tigris dot org
Messages per page: