views:

63

answers:

3

Hello

I am writing a Junit test framework to test web services.

There is a requirement for the input values to come from many different sources, eg an earlier web service call or literals within the class.

To achieve this I have constructors that accept the different inputs in different ways; all simple so far.

The problem is the webservices also need to be exercised with a full data load and a mandatory fields only payload.

Rather then litter the (in some cases verrry long) tests with if statements deciding whether to set a value or not, I have written an annotation @Optional.

Adding this annotation causes it to be nulled by the following code:

     /**
     * Find all of the fields annotated with optional and null them
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     */
    private void blankOptionalFields() throws IllegalAccessException{

        for(Field field: this.getClass().getDeclaredFields()){


            Annotation optionalAnnotation = field.getAnnotation(Optional.class);

            if(!(field.isSynthetic()) && optionalAnnotation instanceof Optional){

                field.setAccessible(true);
                try{
                    field.set(this, null);
                }
                catch(IllegalArgumentException e){
                    logger.debug("Tried to set a scalar field to null!", e);
                }
            }
        }
    }

So two things:

1: Although this works it somehow feels fragile/dangerous, there must be a better approach? 2: If this is not a carzy approach, what is the best way to go about setting the scalar values to appropiate values?

A: 

I would refactor the tests, splitting out the initialization code from the actual test code. For that matter, you could put the actual test code (the code that invokes the web service) into a method that is shared between multiple test methods.

As an semi-related comment: I would think of "unit" tests as exercising the service methods stand-alone, while "integration" tests would exercise it as an actual web service.

kdgregory
I like the idea of splitting the initialization code out, but unfortunately the actual test code can't be shared. Fair point about the "unit" tests
Neil Foley
A: 

I'm not enamored with this approach because you're mixing test code in with your production code.

If you know which fields are mandatory ahead of time, is it possible to just loop through those fields at set them without a complicated if structure?

Kevin
There is no production code involved, its all test code. I can loop through the fields and set them but set them from what source? I'll have to write the setting code for each input
Neil Foley
+1  A: 

How about defining an interface containing just the one method that blanks out optional attributes? You can test an object for implementing the interface and call the method directly.

This handles specific exceptions more elegantly than trying to create a catch all situation using reflection:

interface Blankable {

    /** @return true if all optional fields are successfully blanked. **/
    public boolean blankOptionalFields();
}

and use like:

if (obj implements Blankable) {

    if (!((Blankable) obj).blankOptionalFields()) {

        logger.debug("Could not blank optional fields for " + obj);
    }
}
rsp
I was blinded to this solution by a reflection. It seems logical to me not sure why I didn't think of this first.
Neil Foley