Introduction

Using PODAM in your project

To see how you add PODAM to your project, please refer to the usage page.

How to use PODAM API

PodamFactory factory = new PodamFactoryImpl(); //This will use the default Random Data Provider Strategy
Pojo myPojo = factory.manufacturePojo(Pojo.class);

PODAM allows users to customise the way data are assigned in several ways:

  • By defining a global strategy
  • By defining strategies at the attribute level
  • For primitive and wrapper types, by customising the numeric value through annotations
  • For strings by customising the string value and length through annotation
  • To skip a certain attribute by using the @PodamExclude annotation

Defining a global data provider strategy

The default strategy for PODAM is Random values. However users can define their own global strategy by providing an implementation of the DataProviderStrategy interface, as follows:

DataProviderStrategy strategy = new MyDataProviderStrategy();
PodamFactory factory = new PodamFactoryImpl(strategy);

Pojo myPojo = factory.manufacturePojo(Pojo.class);

Please note that annotations are not required to use PODAM. The tool will automatically introspect and fill your tree of POJOs out-of-the-box. The preferred way to customise the way PODAM fills your data is through a custom DataProviderStrategy, as shown above. However one can annotate Java classes to provide custom values, as shown below.

Defining an attribute-level strategy

PODAM allows also users to define data strategies at the attribute level: this includes the capability to define custom strategies for elements and keys in collections, maps and arrays. In order to define an attribute-level strategy, users will need to:

  • Provide an implementation of the AttributeStrategy<T> interface
  • Use the @PodamStrategyValue annotation

    Example:

    @PodamStrategyValue(PostCodeStrategy.class)
    private String postCode;
    
    @PodamStrategyValue(MyBirthdayStrategy.class)
    private Calendar myBirthday;
    

    In this example I defined two attribute-level strategies:

  • PostCodeStrategy to create a UK-like postcode
  • MyBirthdayStrategy to create a Calendar which contains the exact time of my birth

    The PostCodeStrategy class looks like the following:

    /**
     * 
     */
    package uk.co.jemos.podam.test.strategies;
    
    import uk.co.jemos.podam.api.AttributeStrategy;
    import uk.co.jemos.podam.exceptions.PodamMockeryException;
    import uk.co.jemos.podam.test.utils.PodamTestConstants;
    
    /**
     * A test strategy to manufacture UK-like post codes.
     * 
     * @author mtedone
     * 
     */
    public class PostCodeStrategy implements AttributeStrategy<String> {
            
            /**
             * It returns an English post code.
             * <p>
             * This is just an example. More elaborated code could the implemented by
             * this method. This is just to proof the point.
             * </p>
             * 
             * {@inheritDoc}
             */
            public String getValue() throws PodamMockeryException {
                    return PodamTestConstants.POST_CODE;
            }
            
    }
    

    There is nothing special about the above class: it's job is just to provide a value of the right type.

    The MyBirthdayStrategy class looks like the following:

    /**
     * 
     */
    package uk.co.jemos.podam.test.strategies;
    
    import java.util.Calendar;
    
    import uk.co.jemos.podam.api.AttributeStrategy;
    import uk.co.jemos.podam.exceptions.PodamMockeryException;
    import uk.co.jemos.podam.test.utils.PodamTestUtils;
    
    /**
     * An attribute strategy which returns a Calendar object set with my DOB.
     *
     * @author mtedone
     * 
     */
    public class MyBirthdayStrategy implements AttributeStrategy<Calendar> {
    
            /**
             * It returns a {@link Calendar} object set with the exact date of my
             * birthday.
             * 
             * {@inheritDoc}
             */
            public Calendar getValue() throws PodamMockeryException {
    
                    Calendar myBirthday = PodamTestUtils.getMyBirthday();
    
                    return myBirthday;
            }
                    
    }
    
    

    The capability to customise the strategy PODAM uses to fill attribute data is extended to container-like data structures, such as Collections, Maps and Arrays; however for these data structures use the @PodamCollection annotation. Please refer to The annotation page for more details.

    Example on how to use PODAM custom attribute strategy to fill collection elements:

    @PodamCollection(nbrElements = 2, collectionElementStrategy = MyBirthdayStrategy.class)
    private List<Calendar> myBirthdays = new ArrayList<Calendar>();
    

    Example on how to use PODAM custom attribute strategy to fill Map keys and elements. Please note that @PodamCollection offers also the possibility to define a strategy to set the values for keys in a Map, through the mapKeyStrategy attribute:

    @PodamCollection(nbrElements = 2, mapElementStrategy = MyBirthdayStrategy.class)
    private Map<String, Calendar> myBirthdaysMap = new HashMap<String, Calendar>();
    

    Example on how to use PODAM custom attribute strategy to fill array elements:

    @PodamCollection(nbrElements = 2, collectionElementStrategy = MyBirthdayStrategy.class)
    private Calendar[] myBirthdaysArray;
    

    PODAM will use the MyBirthdayStrategy to set the value for the two array elements.

Customising PODAM behaviour for primitives and wrapper types

Primitive and Wrapper type values can be customised through annotations.

@PodamDoubleValue(minValue = PodamTestConstants.NUMBER_DOUBLE_MIN_VALUE, maxValue = PodamTestConstants.NUMBER_DOUBLE_MAX_VALUE)
private double doubleFieldWithMinAndMaxValue;

Please note that by contract (DataProviderStrategy) min and max values are inclusive.

For a full list of supported annotations, please refer to the Annotations page.

To know more about how PODAM works, please refer to the The walk-through example page or to the Corner Cases page on the left menu.

A simple example to manufacture collections

You can use PODAM to fill in generic collections with dummy data.

PodamFactory podam = new PodamFactoryImpl();

// Annotation required to remove warning because of type erasure although it would work without
@SuppressWarnings("unchecked")
List<String> list = podam.manufacturePojo(ArrayList.class, String.class);

Manufacturing generic types

Sometimes you may want to manufacture a generic type directly without a wrapper class or a subclass.

As you should have noticed, the manufacturePojo method of the PodamFactory interface can take some additional parameters through varargs. This is a way to allow the definition of the generic types for a given class.

The following class is an example of a generic POJO:

public class GenericPojo<F, S> {

        private F firstValue;
        private S secondValue;
        @PodamCollection(nbrElements = 2)
        private List<F> firstList;
        @PodamCollection(nbrElements = 2)
        private S[] secondArray;
        @PodamCollection(nbrElements = 2)
        private Map<F, S> firstSecondMap;
        
        ... getter, setters and toString
}

Now imagine that you want an instance of this class defining F and S as Double and String respectively. This is how it should be done:

GenericPojo pojo = factory.manufacturePojo(GenericPojo.class, Double.class, String.class);

As you can see, the first class is the POJO to be manufactured (GenericPojo), and the subsequent classes are the type parameters to be used for this GenericPojo instance.

This is the result of the call above:

GenericPojo [
    firstValue=0.03181392348712919,
    secondValue=L4OicBAhKY,
    firstList=[0.770082469177622, 0.7924208229673068],
    secondArray=[8EsGFHtwwE, Zqz8B_4oAr],
    firstSecondMap={0.778926356384843=sl4FxQIEYe, 0.2909259461070528=j8mlZshNsv}
]

It is also possible to specify some complex nested generic types like GenericPojo<GenericPojo<String, Long>, Map<Integer, List<Boolean>>> using the uk.co.jemos.podam.api.PodamParameterizedType class as in the following example:

ParameterizedType stringLongGenericPojoType =
                new PodamParameterizedType(GenericPojo.class, String.class, Long.class);

ParameterizedType IntegerBooleanListMapType =
                new PodamParameterizedType(Map.class,
                                Integer.class,
                                new PodamParameterizedType(List.class, Boolean.class));

GenericPojo<GenericPojo<String, Long>, Map<Integer, List<Boolean>>> pojo =
                factory.manufacturePojo(GenericPojo.class, stringLongGenericPojoType, IntegerBooleanListMapType);

Running the code above the following result is produced:

GenericPojo [
    firstValue=GenericPojo [
        firstValue=e0NI7ZnKfW,
        secondValue=1340165351073,
        firstList=[2UlUwNJdOS, bZ7X0KZWtV],
        secondArray=[1340165351073, 1340165351073],
        firstSecondMap={QiB36seL99=1340165351072, Dk_KwHCGOq=1340165351072}
    ],
    secondValue={2081811337=[true]},
    firstList=[
        GenericPojo [
            firstValue=ECNfFuF6WM,
            secondValue=1340165351067,
            firstList=[QfZXnqOk1N, HDAGfxdat0],
            secondArray=[1340165351067, 1340165351067],
            firstSecondMap={UgJ4VNvro_=1340165351067, 5NGtW0hH0h=1340165351067}
        ],
        GenericPojo [
            firstValue=kHryFVavEn,
            secondValue=1340165351070,
            firstList=[wAKe40d3zO, OH8q03P9Cw],
            secondArray=[1340165351070, 1340165351070],
            firstSecondMap={LhRoWR2DNX=1340165351068, zg0IAN4ZZ5=1340165351068}
        ]
    ],
    secondArray=[{2115657799=[true, true], -1507554610=[true, true]}, {1990372468=[true, true], -340173617=[true, true]}],
    firstSecondMap={
        GenericPojo [
            firstValue=WeEDkbr2pc,
            secondValue=1340165351058,
            firstList=[0TRSKWur8n, vbIptzObXA],
            secondArray=[1340165351057, 1340165351057],
            firstSecondMap={49wLsEiuip=1340165351056, LGIUi7CptX=1340165351056}
        ] = {1126207473=[true, true], -1973087621=[true, true]},
        GenericPojo [
            firstValue=2RtNOrjgjZ,
            secondValue=1340165351064,
            firstList=[l1S7URCgFT, ntoKQnI1Kj],
            secondArray=[1340165351064, 1340165351064],
            firstSecondMap={F0gqzGJMZL=1340165351061, vXdlU9NCfR=1340165351061}
        ] = {-485029362=[true, true], 609129754=[true, true]}
    }
]

Using PODAM with Spring

You can also use PODAM with Spring:

  • Define the Data Provider Strategy as a bean if you don't want the default (Random data)
  • Define PodamFactory bean, initialised with the Data Provider Strategy
  • Use the PodamFactory bean in your code
# Define your Spring app context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">


        <!-- The definition of the default strategy -->
        <bean id="myDataProviderStrategy"
                class="uk.co.jemos.podam.api.MyDataProviderStrategy"
                factory-method="getInstance" />
                
        <!-- The definition of PODAM factory -->
        <bean id="podamFactory" class="uk.co.jemos.podam.api.PodamFactoryImpl">
          <constructor-arg index="0" ref="myDataProviderStrategy" />
        </bean>

</beans>
# Use Podam Factory in your code

/**
 * 
 */
package uk.co.jemos.podam.test.unit.integration;

import javax.annotation.Resource;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

import uk.co.jemos.podam.api.PodamFactory;
import uk.co.jemos.podam.test.dto.SimplePojoToTestSetters;

/**
 * @author mtedone
 * 
 */
@ContextConfiguration(locations = {"classpath:podam-test-appContext.xml"})
public class PodamFactoryInjectionIntegrationTest
                extends AbstractJUnit4SpringContextTests {
        
        /** The Podam Factory */
        @Resource
        private PodamFactory factory;
        
        @Before
        public void init() {
                Assert.assertNotNull("The PODAM factory cannot be null!", factory);
                Assert.assertNotNull("The factory strategy cannot be null!",
                                factory.getStrategy());
        }

        @Test
        public void testSimplePojo() {

                SimplePojoToTestSetters pojo = factory
                                .manufacturePojo(SimplePojoToTestSetters.class);
                Assert.assertNotNull("The pojo cannot be null!", pojo);

                ...etc
        }
}

Please note that the data provider strategy cannot be changed upon instantiation. If you want to use a different data provider strategy, you'll need to create a new instance of PodamFactory. This has been decided to preserve thread-safety and immutability of PodamFactory.

How PODAM assigns values

By default PODAM assigns random values to all its types. However, PODAM can be extended by providing a custom implementation of the uk.co.jemos.podam.api.DataProviderStrategy interface. Please note that methods which return a numeric value between ranges should consider the ranges inclusive, as documented in the interface Javadoc.

PODAM uses the uk.co.jemos.podam.api.RandomDataProviderStrategy class as default Data Provider implementation.

To know more about how PODAM works, please refer to the The walk-through example page or to the Corner Cases page on the left menu.