RE: Testing strategies

2007-11-22 Thread Kyrre Kristiansen
A strategy I've seen that's worked great is a mix
between interfaces and private implementations. This
would require an associated Factory to look up the
actual implementation. This gives you the choice of
stubbing out the interface if it's needed, or creating
mock objects for the different private
implementations. 

Maybe this would be a choice for when the project
becomes more stable on the external inteface side?

Kyrre

--- Jerome Louvel [EMAIL PROTECTED] wrote:

 
 Agreed, I've entered a design task for 2.0 M1 to
 remember this point:
 http://restlet.tigris.org/issues/show_bug.cgi?id=384
 
 Best regards,
 Jerome  
 
  -Message d'origine-
  De : [EMAIL PROTECTED]
 [mailto:[EMAIL PROTECTED] De la 
  part de Tim Peierls
  Envoyé : mardi 20 novembre 2007 20:33
  À : discuss@restlet.tigris.org
  Objet : Re: Testing strategies
  
  On Nov 20, 2007 1:55 PM, Sean Landis
 [EMAIL PROTECTED] wrote:
  
  
  Stated differently, maybe when the convergence is
 over, 
  it will be much safer to move to interfaces. The
 nearly 
  immutable nature of interfaces won't be such an
 issue since 
  the API questions will have mostly been answered. 
  
  
  
  And presumably a compatibility layer could be
 maintained, 
  at least through 2.x, to help 1.x clients migrate
 at their own speed.
  
  --tim
  
  
 



Kyrre Kristiansen


  ___
Yahoo! Answers - Got a question? Someone out there knows the answer. Try it
now.
http://uk.answers.yahoo.com/ 


Re: Testing strategies

2007-11-20 Thread Tim Peierls
On Nov 20, 2007 9:06 AM, Jerome Louvel [EMAIL PROTECTED] wrote:

 Interfaces are useful in published APIs when they contain a very small and
 stable set of methods that you know won't need to expand.


Agreed.


A good example is the java.lang.Comparable interface.

 A counter example is the List interface. Imagine that Sun adds new methods
 to it, how many lines of code would need to be changed to accommodate the
 evolution? How many Applets/Servlets would break?


That's not a counter-example, it's another example. No one should be
thinking about changing List, any more than they should be thinking about
changing Comparable -- too much code depends on it being stable. The
advantages of being able to pass around Lists and Maps instead of Vectors
and Hashtables have been proven. The fact that no one can ever change the
List interface makes it *more* appropriate for use in public APIs.


Other reasons to prefer classes over interfaces:

 1) It makes the deprecation of methods easier because you can make the new
 method call the old logic (or the other way around), facilitating the
 progressive migration to the new method (like the core Java API do).


OK, but this is just a corollary of the fact that interfaces are forever.
If you really need to be constantly modifying a published API and yet retain
complete backward compatibility with earlier versions, interfaces do indeed
cause problems. (But should mutability of an API be the primary design
concern?)


2) It reduces the conceptual weight of the API, no factories, no
 base/abstract classes to provide/understand.


I disagree. A well-designed API involving interfaces is usually *easier* to
understand *because* you don't have to learn about lots of types and
implementation details. A good dependency injection framework can virtually
eliminate the need for factories.



 3) It allows the sharing of 'behavior' instead of just method
 signatures/contract with subclasses.


This could just as easily go in the cons section. Subclasses are *forced*
to share behavior with the parent class (or override it), which increases
coupling.


Here are the main cons I see:

 1) Can't extend multiple parent classes

 2) Not easily mockable


3) It is very difficult to design classes for extension, especially
thread-safe classes.

I'm not trying to talk you out of the current Restlet design, but I would
rather have it described as the result of some tough design decisions on
trade-offs concerning evolvability and implementation inheritance, not as
classes are better than interfaces in APIs.

--tim


RE: Testing strategies

2007-11-20 Thread Jerome Louvel

Hi Tim,

 That's not a counter-example, it's another example. No one 
 should be thinking about changing List, any more than they 
 should be thinking about changing Comparable -- too much code 
 depends on it being stable. The advantages of being able to 
 pass around Lists and Maps instead of Vectors and Hashtables 
 have been proven. The fact that no one can ever change the 
 List interface makes it *more* appropriate for use in public APIs. 

There is indeed a lot of value in the List artifact. But it could very well
have been provided as an abstract List class. 

I do see your point about usage of interfaces to be explicit about the fact
that no evolution is possible in the future.

  1) It makes the deprecation of methods easier...

 OK, but this is just a corollary of the fact that interfaces 
 are forever. If you really need to be constantly modifying a 
 published API and yet retain complete backward compatibility 
 with earlier versions, interfaces do indeed cause problems. 
 (But should mutability of an API be the primary design concern?) 

For an API such as the Collections API, it might be possible to fully
embrace the features scope in one version, but for the Restlet case it
didn't seem reasonable despite the fact that we had a lot of initial tests
and feed-back before releasing v1.

  2) It reduces the conceptual weight of the API...

 I disagree. A well-designed API involving interfaces is 
 usually *easier* to understand *because* you don't have to 
 learn about lots of types and implementation details. A good 
 dependency injection framework can virtually eliminate the 
 need for factories. 

Even if the public contract is a simple interface, don't you still need to
understand the implementation details of the injected implementers ?

  3) It allows the sharing of 'behavior'...
 
 This could just as easily go in the cons section. 
 Subclasses are *forced* to share behavior with the parent 
 class (or override it), which increases coupling.

In the Restlet API, most classes are quite light in term of behavior. The
hard logic is actually externalized in an engine via a SPI/'helper'
mechanism (see org.restlet.util.Engine and org.restlet.util.Helper).

 3) It is very difficult to design classes for extension, 
 especially thread-safe classes. 
 
 I'm not trying to talk you out of the current Restlet design, 
 but I would rather have it described as the result of some 
 tough design decisions on trade-offs concerning evolvability 
 and implementation inheritance, not as classes are better 
 than interfaces in APIs. 

This is a better description of the subject indeed, and one that reflects
the design process that took place to design the v1 of the Restlet API. For
example, the initial versions had interfaces for all the artifacts.
Feed-back, discussions, readings led me to reconsider this approach.

I'm open to try a different path for the design of the v2 of the API. At
this point, we will have more experience on our problem domain and we will
be able to afford more radical changes. 

Best regards,
Jerome


Re: Testing strategies

2007-11-20 Thread Sean Landis

  I'm not trying to talk you out of the current Restlet design, 
  but I would rather have it described as the result of some 
  tough design decisions on trade-offs concerning evolvability 
  and implementation inheritance, not as classes are better 
  than interfaces in APIs. 
 
 This is a better description of the subject indeed, and one that reflects
 the design process that took place to design the v1 of the Restlet API. For
 example, the initial versions had interfaces for all the artifacts.
 Feed-back, discussions, readings led me to reconsider this approach.
 
 I'm open to try a different path for the design of the v2 of the API. At
 this point, we will have more experience on our problem domain and we will
 be able to afford more radical changes. 

I had pushed a little bit against the move away from interfaces. I didn't like
it but I understood that a design goal of Restlet was to converge onto an 
API through
experience rather than try to get it right up front. Given that poorly charted
territory was being navigated, I felt this was a helpful approach. 

I am glad to hear Jerome, that you are willing to reconsider
the decision about interfaces. Stated differently, maybe when the convergence
is over, it will be much safer to move to interfaces. The nearly immutable
nature of interfaces won't be such an issue since the API questions will have
mostly been answered.

Sean




Re: Testing strategies

2007-11-20 Thread Tim Peierls
On Nov 20, 2007 1:55 PM, Sean Landis [EMAIL PROTECTED] wrote:

 Stated differently, maybe when the convergence is over, it will be much
 safer to move to interfaces. The nearly immutable nature of interfaces won't
 be such an issue since the API questions will have mostly been answered.


And presumably a compatibility layer could be maintained, at least through
2.x, to help 1.x clients migrate at their own speed.

--tim


Re: Testing strategies

2007-11-19 Thread Jerome Louvel
Hi Justin,

The usage of interfaces is discouraged in public APIs because they prevent
changes in later releases (breaking existing implementation classes).

I'm not familiar with EasyMock, but isn't it possible to generate mock
objects that are subclasses of other classes, like a MockRequest extending a
Request and setting custom values ?

Could you describe a typical test that you would like to write, and we can
discuss how we could write it ?

Best regards,
Jerome


2007/11/18, Justin Makeig [EMAIL PROTECTED]:

 I'm curious if and how others are writing unit tests for Restlet
 applications. In previous J2EE web apps I've written I've made heavy
 use of Easy Mock http://www.easymock.org/ for mocking collaborators
 and replaying scripts to validate behavior. Mocks lend themselves
 very well to interface-centric architectures, such as the style that
 Spring encourages. However, types, such as Context, Request, and
 Response are all concrete classes, requiring runtime contortions to
 mock or stub implementations, for example to set the attributes on a
 Request. The tests in the trunk don't seem very illustrative. Any
 suggestions would be much appreciated. Thanks.

 Justin



Re: Testing strategies

2007-11-19 Thread Chuck Hinson
Jerome, I know you've made this statement before (interfaces in public
APIs), and since it is your project, I've not pressed you about it,
but I'd still like to see some evidence that this is more than just
personal opinion.

As for unit testing Restlets, I've found them much easier to test than
servlets, primarily because there's no need to mock request objects -
you can just create and initialize a Request and feed it directly to
your resource via getRepresentation() (or put(), post(), etc).

--Chuck


On Nov 19, 2007 4:39 AM, Jerome Louvel [EMAIL PROTECTED] wrote:
 Hi Justin,

 The usage of interfaces is discouraged in public APIs because they prevent
 changes in later releases (breaking existing implementation classes).

 I'm not familiar with EasyMock, but isn't it possible to generate mock
 objects that are subclasses of other classes, like a MockRequest extending a
 Request and setting custom values ?

 Could you describe a typical test that you would like to write, and we can
 discuss how we could write it ?

 Best regards,
 Jerome


 2007/11/18, Justin Makeig  [EMAIL PROTECTED]:

  I'm curious if and how others are writing unit tests for Restlet
  applications. In previous J2EE web apps I've written I've made heavy
  use of Easy Mock http://www.easymock.org/ for mocking collaborators
  and replaying scripts to validate behavior. Mocks lend themselves
  very well to interface-centric architectures, such as the style that
  Spring encourages. However, types, such as Context, Request, and
  Response are all concrete classes, requiring runtime contortions to
  mock or stub implementations, for example to set the attributes on a
  Request. The tests in the trunk don't seem very illustrative. Any
  suggestions would be much appreciated. Thanks.
 
  Justin
 




Re: Testing strategies

2007-11-19 Thread Stian Soiland
On 11/18/07, Justin Makeig [EMAIL PROTECTED] wrote:
 I'm curious if and how others are writing unit tests for Restlet
 applications. In previous J2EE web apps I've written I've made heavy
 use of Easy Mock http://www.easymock.org/ for mocking collaborators
 and replaying scripts to validate behavior. Mocks lend themselves
 very well to interface-centric architectures, such as the style that
 Spring encourages. However, types, such as Context, Request, and
 Response are all concrete classes, requiring runtime contortions to
 mock or stub implementations, for example to set the attributes on a
 Request. The tests in the trunk don't seem very illustrative. Any
 suggestions would be much appreciated. Thanks.

We did the junit tests quite life-like in this fashion:

public abstract class ClientTest extends TestCommon {
public static final int PORT = 8977;
public static final String ROOT_URL = http://localhost:; + PORT  + 
/;
public static final String BASE_URL = ROOT_URL + v1/;
private static RestApplication server;
@BeforeClass
public synchronized static void startServer() throws Exception {
stopServer();
server = new RestApplication();
server.startServer(PORT);
}

@AfterClass
public synchronized static void stopServer() throws Exception {
if (server != null) {
server.stopServer();
}
}

@BeforeClass
public synchronized static void registerAdminUser() {
username = null;
useruri = null;
DAOFactory daoFactory = DAOFactory.getFactory();
user = new User();
password = User.generatePassword();
user.setPassword(password);
user.setAdmin(true);
daoFactory.getUserDAO().create(user);
daoFactory.commit();
username = user.getUsername();
useruri = BASE_URL + users/ + username;
System.out.println(Registered  + username +   + password);
}

/**
 * Make an authenticated GET request.
 *
 * @return
 */
public Request makeAuthRequest() {
Request request = new Request();
ChallengeResponse challengeResponse =
new ChallengeResponse(ChallengeScheme.HTTP_BASIC,
username, password);
request.setChallengeResponse(challengeResponse);
request.setMethod(Method.GET);
return request;
}
}

Then a simple test can be like this:

public class TestDataDoc extends ClientTest {
@Test
public void getAllXML() throws IOException, ParseException, 
XmlException {
Request request = makeAuthRequest();
Client client = new Client(Protocol.HTTP);
request.setResourceRef(useruri + /data);
request.setMethod(Method.GET);
request.getClientInfo().getAcceptedMediaTypes().add(
new PreferenceMediaType(restType));
Response response = client.handle(request);
assertTrue(response.getStatus().isSuccess());
DatasDocument doc =

DatasDocument.Factory.parse(response.getEntity().getStream());
for (Data data : doc.getDatas().getDataArray()) {
assertTrue(data.getHref().startsWith(BASE_URL + 
data));
}
}

@Test
public void create() throws IOException {
Request request = makeAuthRequest();
Client client = new Client(Protocol.HTTP);
request.setResourceRef(useruri + /data);
request.setMethod(Method.POST);
request.setEntity(datadoc, baclavaType);
Response response = client.handle(request);
assertTrue(response.getStatus().isSuccess());
justCreated = response.getRedirectRef();
}

@Test
public void createEmpty() throws IOException {
Request request = makeAuthRequest();
Client client = new Client(Protocol.HTTP);
request.setResourceRef(useruri + /data);
request.setMethod(Method.POST);
request.setEntity(, baclavaType);
Response response = client.handle(request);
assertEquals(Status.CLIENT_ERROR_BAD_REQUEST, 
response.getStatus());
assertNull(response.getRedirectRef());
}
}

-- 
Stian Soiland, myGrid team
School of Computer Science
The University of Manchester
http://www.cs.man.ac.uk/~ssoiland/


Re: Testing strategies

2007-11-19 Thread Justin Makeig
Chuck, thanks for the input. In general, yes, Restlets are much  
simpler in their dependencies than servlets and thus easier to test.  
However, my struggle is extra cost of writing stubs vs. mocks http:// 
martinfowler.com/articles/mocksArentStubs.html. Mocks allow one to  
just implement a test version of only the functionality you need as a  
collaborator to the (concrete) class being tested, rather than to  
subclass a stubbed out anemic version of a collaborator  
interface (i.e. its public methods). Additionally, something like  
Easy Mock allows you to replay a test script to ensure that the  
collaborators were used as expected.


Justin

On Nov 19, 2007, at 7:17 AM, Chuck Hinson wrote:


Jerome, I know you've made this statement before (interfaces in public
APIs), and since it is your project, I've not pressed you about it,
but I'd still like to see some evidence that this is more than just
personal opinion.

As for unit testing Restlets, I've found them much easier to test than
servlets, primarily because there's no need to mock request objects -
you can just create and initialize a Request and feed it directly to
your resource via getRepresentation() (or put(), post(), etc).

--Chuck


On Nov 19, 2007 4:39 AM, Jerome Louvel [EMAIL PROTECTED] wrote:

Hi Justin,

The usage of interfaces is discouraged in public APIs because they  
prevent

changes in later releases (breaking existing implementation classes).

I'm not familiar with EasyMock, but isn't it possible to generate  
mock
objects that are subclasses of other classes, like a MockRequest  
extending a

Request and setting custom values ?

Could you describe a typical test that you would like to write,  
and we can

discuss how we could write it ?

Best regards,
Jerome


2007/11/18, Justin Makeig  [EMAIL PROTECTED]:


I'm curious if and how others are writing unit tests for Restlet
applications. In previous J2EE web apps I've written I've made heavy
use of Easy Mock http://www.easymock.org/ for mocking  
collaborators

and replaying scripts to validate behavior. Mocks lend themselves
very well to interface-centric architectures, such as the style that
Spring encourages. However, types, such as Context, Request, and
Response are all concrete classes, requiring runtime contortions to
mock or stub implementations, for example to set the attributes on a
Request. The tests in the trunk don't seem very illustrative. Any
suggestions would be much appreciated. Thanks.

Justin






Re: Testing strategies

2007-11-19 Thread Justin Makeig

Stian,
I would consider what you've helpfully included below an integration,  
not unit test. These are invaluable, but I'm trying to just test  
small pieces of functionality in isolation one at a time, not the  
whole stack. For example, if I were to test my Resource  
implementation's put method, I only want to test its inner workings,  
not all of the other stuff that it uses, such as my service layer or  
actual Representations (presumably these  will be tested elsewhere).
Maybe my approach is inappropriate. I'll do some more thinking and  
coding and post my findings.

Thanks to everyone for the input.

Justin


On Nov 19, 2007, at 9:36 AM, Stian Soiland wrote:


On 11/18/07, Justin Makeig [EMAIL PROTECTED] wrote:

I'm curious if and how others are writing unit tests for Restlet
applications. In previous J2EE web apps I've written I've made heavy
use of Easy Mock http://www.easymock.org/ for mocking collaborators
and replaying scripts to validate behavior. Mocks lend themselves
very well to interface-centric architectures, such as the style that
Spring encourages. However, types, such as Context, Request, and
Response are all concrete classes, requiring runtime contortions to
mock or stub implementations, for example to set the attributes on a
Request. The tests in the trunk don't seem very illustrative. Any
suggestions would be much appreciated. Thanks.


We did the junit tests quite life-like in this fashion:

public abstract class ClientTest extends TestCommon {
public static final int PORT = 8977;
	public static final String ROOT_URL = http://localhost:; + PORT   
+ /;	

public static final String BASE_URL = ROOT_URL + v1/;
private static RestApplication server;
@BeforeClass
public synchronized static void startServer() throws Exception {
stopServer();
server = new RestApplication();
server.startServer(PORT);
}

@AfterClass
public synchronized static void stopServer() throws Exception {
if (server != null) {
server.stopServer();
}
}

@BeforeClass
public synchronized static void registerAdminUser() {
username = null;
useruri = null;
DAOFactory daoFactory = DAOFactory.getFactory();
user = new User();
password = User.generatePassword();
user.setPassword(password);
user.setAdmin(true);
daoFactory.getUserDAO().create(user);
daoFactory.commit();
username = user.getUsername();
useruri = BASE_URL + users/ + username;
System.out.println(Registered  + username +   + password);
}

/**
 * Make an authenticated GET request.
 *
 * @return
 */
public Request makeAuthRequest() {
Request request = new Request();
ChallengeResponse challengeResponse =
new ChallengeResponse(ChallengeScheme.HTTP_BASIC,
username, password);
request.setChallengeResponse(challengeResponse);
request.setMethod(Method.GET);
return request;
}
}

Then a simple test can be like this:

public class TestDataDoc extends ClientTest {
@Test
	public void getAllXML() throws IOException, ParseException,  
XmlException {

Request request = makeAuthRequest();
Client client = new Client(Protocol.HTTP);
request.setResourceRef(useruri + /data);
request.setMethod(Method.GET);
request.getClientInfo().getAcceptedMediaTypes().add(
new PreferenceMediaType(restType));
Response response = client.handle(request);
assertTrue(response.getStatus().isSuccess());
DatasDocument doc =

DatasDocument.Factory.parse(response.getEntity().getStream());
for (Data data : doc.getDatas().getDataArray()) {
assertTrue(data.getHref().startsWith(BASE_URL + 
data));
}
}

@Test
public void create() throws IOException {
Request request = makeAuthRequest();
Client client = new Client(Protocol.HTTP);
request.setResourceRef(useruri + /data);
request.setMethod(Method.POST);
request.setEntity(datadoc, baclavaType);
Response response = client.handle(request);
assertTrue(response.getStatus().isSuccess());
justCreated = response.getRedirectRef();
}

@Test
public void createEmpty() throws IOException {
Request request = makeAuthRequest();
Client client = new Client(Protocol.HTTP);

Re: Testing strategies

2007-11-19 Thread Chuck Hinson
On Nov 19, 2007 3:35 PM, Jerome Louvel [EMAIL PROTECTED] wrote:

 Hi Chuck,

  Jerome, I know you've made this statement before (interfaces in public
  APIs), and since it is your project, I've not pressed you about it,
  but I'd still like to see some evidence that this is more than just
  personal opinion.

 Initially, the API had many interfaces but some (Henry Story I think)
 pointed me that public APIs based on interfaces are fragile because once
 they are published they can't easily evolve.

 If you have interface A with method A1 in version 1.0, then users will
 implement it in their custom classes, this is fine, they can easily mock
 them.

 Then, for version 2.0, the need to add method A2 appears and is added to the
 A interface. When the API is published, users try to upgrade and realize
 that their custom classes don't implement method A2, or implement it with a
 different purpose or different arguments. Their code is broken and the API
 break the ascendant compatibility.
[...]
I see the point, but remain unconvinced; with modern refactoring and
dependency management tools, I'm not sure that this is an actual
problem.

I'd also point out that many people view the addition of a method to a
base class (abstract or otherwise) as a breaking change (because
existing clients may have already implemented a method with the same
signature in existing subclasses).

If you change an API, it doesnt seem unreasonable to me to expect
that implementors will have to change their implementation.

To me, it seems like a case of throwing the baby out with the bath
water.  Versioning's hard, and there are appropriate and inappropriate
uses of interfaces, but to make a blanket statement that interfaces
shouldn't be used in a published API seems unwarranted.

I was actually hoping for a pointer to a discussion about the pros and
cons of using abstract classes instead of the interfaces in published
APIs, but I haven't been able to find anything - which makes me wonder
whether it's really that big a deal.

--Chuck