También pueden echar un vistazo a testUtils, es una libreria interesante que les ayudara a realizar los test unitarios de una manera más elegante y sin escribir tanta redundancia de código:
package com.hector; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.Embeddable; import javax.persistence.Entity; import org.junit.Assert; import org.junit.Test; import org.powermock.reflect.Whitebox; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Test automatique des getter/setter. A voir si on s'en sert. */ public class BeansAutomatedTest { /** logger */ private static final Logger LOGGER = LoggerFactory .getLogger(BeansAutomatedTest.class); /** prefixes possibles pour les noms des getters */ public static final String[] POSSIBLE_GETTER_PREFIXES = { "get", "is", "has" }; /** prefixes possibles pour les noms des setters */ private static final String[] POSSIBLE_SETTER_PREFIXES = { "set" }; /** * map des implémentations à utiliser pour instancier des interfaces. Ex: * List --> ArrayList */ private static final Map <Class <?>, Class <?>> IMPLEMENTATIONS_TO_USE = new HashMap <Class <?>, Class <?>>(); /** * map des wrapper de types primitifs à utiliser pour tester les * getter/setter sur des Integer, Boolean... */ private static final Set <Class <?>> BOXED_PRIMITIVES = new HashSet <Class <?>>(); /** * map des primitifs à utiliser pour tester les getter/setter sur des types * primitifs int, boolean... */ private static final Map <Class <?>, Object> PRIMITIVES_TO_USE = new HashMap <Class <?>, Object>(); /** map des tableaux de champs à ignorer par classe */ private static final Map <String, List <String>> IGNORED_FIELDS = new HashMap <String, List <String>>(); /** list des classes ignorées */ private static final List <String> IGNORED_BEANS = new ArrayList <String>(); private static enum TypeTest { Entities, Representations, Dto, Vo } static { IMPLEMENTATIONS_TO_USE.put(List.class, ArrayList.class); IMPLEMENTATIONS_TO_USE.put(Set.class, HashSet.class); IMPLEMENTATIONS_TO_USE.put(Collection.class, ArrayList.class); IMPLEMENTATIONS_TO_USE.put(Map.class, HashMap.class); BOXED_PRIMITIVES.add(Integer.class); BOXED_PRIMITIVES.add(Long.class); BOXED_PRIMITIVES.add(Character.class); BOXED_PRIMITIVES.add(Double.class); BOXED_PRIMITIVES.add(Float.class); BOXED_PRIMITIVES.add(Boolean.class); BOXED_PRIMITIVES.add(BigDecimal.class); PRIMITIVES_TO_USE.put(int.class, 0); PRIMITIVES_TO_USE.put(Integer.class, 0); PRIMITIVES_TO_USE.put(long.class, 0L); PRIMITIVES_TO_USE.put(Long.class, 0L); PRIMITIVES_TO_USE.put(char.class, '\0'); PRIMITIVES_TO_USE.put(Character.class, '\0'); PRIMITIVES_TO_USE.put(boolean.class, true); PRIMITIVES_TO_USE.put(Boolean.class, Boolean.TRUE); PRIMITIVES_TO_USE.put(float.class, 0f); PRIMITIVES_TO_USE.put(Float.class, 0f); PRIMITIVES_TO_USE.put(double.class, 0d); PRIMITIVES_TO_USE.put(Double.class, 0d); PRIMITIVES_TO_USE.put(BigDecimal.class, BigDecimal.ZERO); // Les champs ci-dessous sont modifiés à leur set ou à leur get // TODO tester ces getters/setters unitairement IGNORED_FIELDS .put("com.hector.initialization.portefeuilledecommandes.PortefeuilleDeCommandesDto", Arrays.asList(new String[] { "paysGamme", "paysCommerce", "paysProgramme" })); IGNORED_FIELDS .put("com.hector.batch.referentiels.produit.bcv.BcvRequestHeaderDto", Arrays.asList(new String[] { "header" })); IGNORED_FIELDS.put( "com.hector.domain.centredemontage.JourDeProduction", Arrays.asList(new String[] { "dateJourProd" })); IGNORED_FIELDS .put("com.hector.rest.commande.RechercheCommandePlanifieRepresentation", Arrays.asList(new String[] { "familles" })); IGNORED_FIELDS .put("com.hector.cycleprogramme.AppariementRepresentation", Arrays.asList(new String[] { "alerte" })); IGNORED_FIELDS .put("com.hector.rest.cycleprogramme.CycleProgrammeRepresentation", Arrays.asList(new String[] { "numSeqAnnee" })); IGNORED_FIELDS .put("com.hector.rest.seuilsAlerteventes.SeuilsAlerteVentesRepresentation", Arrays.asList(new String[] { "codePcom" })); IGNORED_FIELDS .put("com.hector.rest.traitement.TraitementDeDefilementRepresentation", Arrays.asList(new String[] { "annee", "Numordre" })); IGNORED_FIELDS .put("com.hector.rest.centredemontage.DifferentielcalendrierRepresentation", Arrays.asList(new String[] { "dateJourProdstring" })); // Beans ignorés // TODO tester ces beans unitairement IGNORED_BEANS .add("com.hector.batch.diffusion.evn.EVNRequestFooterDto"); IGNORED_BEANS .add("com.hector.batch.diffusion.evn.EVNRequestHeaderDto"); IGNORED_BEANS .add("com.hector.batch.diffusion.ftr.FDPFtrRequestBodyDto"); IGNORED_BEANS .add("com.hector.batch.diffusion.sisif.SisifDiffOfEnvoyeUsineRequestBodyDto"); IGNORED_BEANS .add("com.hector.batch.luoreactualisee.PostEusiKeyDto"); IGNORED_BEANS .add("com.hector.batch.replanification.officialisation.IodaDto"); IGNORED_BEANS.add("com.hector.rest.PEDBaseRepresentation"); IGNORED_BEANS .add("com.hector.rest.chart.XDateYIntSizeValuesRepresentation"); IGNORED_BEANS .add("com.hector.rest.chart.XDateYIntValuesRepresentation"); IGNORED_BEANS .add("com.hector.rest.chart.XDateYIntZIntValuesRepresentation"); IGNORED_BEANS .add("com.hector.rest.pfab.PossibiliteFabricationRepresentation"); IGNORED_BEANS .add("com.hector.rest.replanification.AttributionCessionModeRepresentation"); IGNORED_BEANS .add("com.hector.rest.replanification.AttributionCessionSemaineRepresentation"); IGNORED_BEANS .add("com.hector.rest.replanification.AvanceRetardVolumeCafRepresentation"); IGNORED_BEANS .add("com.hector.rest.replanification.CommandesEspaceAttenteRepresentation"); IGNORED_BEANS .add("com.hector.rest.replanification.PopulationSFPRepresentation"); IGNORED_BEANS .add("com.hector.rest.replanification.ResultatRessourceRepresentation"); IGNORED_BEANS .add("com.hector.rest.replanification.SuiviQualitatifReplanifRepresentation"); IGNORED_BEANS .add("com.hector.rest.ressource.AleaRessourceRepresentation"); IGNORED_BEANS.add("com.hector.rest.slot.SlotVoRepresentation"); IGNORED_BEANS.add("com.hector.rest.slot.TimeLineRepresentation"); } @Test public void testPEDAppDtos() { new PEDBeansAutomatedTest().test("com.hector.app", TypeTest.Dto); } @Test public void testPEDBatchDtos() { new PEDBeansAutomatedTest().test("com.hector.batch", TypeTest.Dto); } @Test public void testPEDDomainEntities() { new PEDBeansAutomatedTest().test("com.hector.domain", TypeTest.Entities); } @Test public void testPEDDomainVos() { new PEDBeansAutomatedTest().test("com.hector.domain", TypeTest.Vo); } @Test public void testPEDWebRepresentations() { new PEDBeansAutomatedTest().test("com.hector.rest", TypeTest.Representations); } /** * Méthode principale de test. <br> * 1. Collecte les entités. <br> * 2. Teste les entités. <br> * 3. Affiche les résultats. <br> * * @param domainPkg * package contenant les entités à tester. Seul un niveau * d'arborescence est supporté pour l'instant (domain/container, * domain/magasin....) */ public void test(String domainPkg, TypeTest typeTest) { List <Class <?>> entitiesToTest = collectAllEntities(domainPkg, typeTest); TestResult allTestsResults = new TestResult(); for (Class <?> entity : entitiesToTest) { if (IGNORED_BEANS.contains(entity.getName())) { LOGGER.debug("Bean {} ignored", entity.getName()); allTestsResults.addIgnoredBean(); } else { allTestsResults.merge(testEntity(entity)); } } LOGGER.info("{} beans of type {} have been tested in {}", entitiesToTest.size(), typeTest, domainPkg); LOGGER.info(" --> {} beans ignored", allTestsResults.getNbBeansIgnored()); LOGGER.info(" --> {} fields tested OK", allTestsResults.getNbTestedSucceeded()); LOGGER.info(" --> {} fields tested KO", allTestsResults.getNbTestedFailed()); LOGGER.info( " --> {} fields could not be tested due to primitive/implementation issues", allTestsResults.getNbNotTestedDueToPrimitiveOrImp()); LOGGER.info(" --> {} fields could not be tested due to other issues", allTestsResults.getNbNotTestedOther()); LOGGER.info(" --> {} fields ignored", allTestsResults.getNbIgnored()); Assert.assertEquals(0, allTestsResults.getNbTestedFailed()); Assert.assertEquals(0, allTestsResults.getNbNotTestedDueToPrimitiveOrImp()); Assert.assertEquals(0, allTestsResults.getNbNotTestedOther()); } /** * try to test the getter / setter of the entity. Return true if it was * tested * * @param entityClass * entity to test * @return number of tested getter/setter pairs */ private TestResult testEntity(Class <?> entityClass) { LOGGER.debug("Trying to test {}", entityClass.getName()); List <String> ignoredFields = (IGNORED_FIELDS.containsKey(entityClass .getName())) ? IGNORED_FIELDS.get(entityClass.getName()) : new ArrayList <String>(); TestResult testResultClass = new TestResult(); try { Object entityInstance = tryToInstantiateClass(entityClass); if (entityInstance == null) { LOGGER.warn("Bean {} could not be instantiated.", entityClass.getName()); testResultClass.merge(TestResultType.NOT_TESTED_DUE_TO_OTHER .getTestResult()); } else { for (Field field : entityClass.getDeclaredFields()) { if (ignoredFields.contains(field.getName())) { LOGGER.debug("Field {} of {} ignored.", field.getName(), entityClass.getName()); testResultClass.merge(TestResultType.IGNORED .getTestResult()); } else { Method getter = this.findGetter(entityClass, field); Method setter = this.findSetter(entityClass, field); if (getter != null && setter != null) { LOGGER.debug( "Getter and setter found for field {}.", field.getName()); TestResultType oneTest = setThenGet(entityInstance, field, setter, getter); testResultClass.merge(oneTest.getTestResult()); } } } } } catch (SecurityException e) { LOGGER.warn(e.getMessage(), e); } catch (IllegalArgumentException e) { LOGGER.warn(e.getMessage(), e); } return testResultClass; } /** * Set puis get un objet. Vérifie que l'objet setté est bien l'objet getté. * * @param entityInstance * instance de l'entité testée * @param field * attribut de l'instance * @param setter * setter de l'attribut * @param getter * getter de l'attribut * @return résultat du test */ private TestResultType setThenGet(Object entityInstance, Field field, Method setter, Method getter) { TestResultType result = TestResultType.NOT_TESTED_DUE_TO_OTHER; try { Class <?> type = field.getType(); Object objectToSet = createInstanceOfType(type); if (objectToSet == null) { LOGGER.warn("Class {} : could not create {}", entityInstance .getClass().getName(), field.getName()); result = TestResultType.NOT_TESTED_DUE_TO_PRIMITIVE_OR_IMPLEMENTATION; } else { // on fait un set puis un get. On vérifie ensuite que l'objet // qu'on a set est bien celui qu'on get. setter.invoke(entityInstance, objectToSet); Object resultOfGet = getter.invoke(entityInstance); // String msg = "Class " + entityInstance.getClass().getName() + // ": one of " + getter.getName() + "()/" // + // setter.getName() // + "() is wrong.\n" + // "The getter do not return what the setter set."; // if (!type.isPrimitive()) { // Assert.assertTrue(msg, resultOfGet == objectToSet); // } else { // Assert.assertEquals(msg, objectToSet, resultOfGet); // } if ((!type.isPrimitive() && resultOfGet != objectToSet) || (type.isPrimitive() && !resultOfGet .equals(objectToSet))) { LOGGER.warn("Class " + entityInstance.getClass().getName() + ": one of " + getter.getName() + "()/" + setter.getName() + "() is wrong.\n" + "The getter do not return what the setter set."); // AssertEquals(monObjetGet, monObjetSet); result = TestResultType.TESTED_AND_FAILED; } else { LOGGER.debug("!!! successfully tested {}", field.getName()); result = TestResultType.TESTED_AND_SUCCEEDED; } } } catch (IllegalAccessException e) { LOGGER.warn(e.getMessage(), e); } catch (IllegalArgumentException e) { LOGGER.warn(e.getMessage(), e); } catch (InvocationTargetException e) { LOGGER.warn(e.getMessage(), e); } catch (SecurityException e) { LOGGER.warn(e.getMessage(), e); } return result; } /** * Crée une instance de l'objet de type type ou fournis un objet par défaut * si c'est une primitive, une enum, etc... * * @param type * le type * @return une instance/une primitive/une enum */ protected Object createInstanceOfType(Class <?> type) { Object objectToSet = null; try { // si c'est une enum, on retourne la 1ere constante de l'enum if (type.isEnum()) { List <?> enumConstants = Arrays.asList(type.getEnumConstants()); objectToSet = enumConstants.get(0); } // si c'est une primitive ou une boxed primitive, on utilise un // objet par défaut else if (type.isPrimitive() || typeIsBoxedPrimitive(type)) { objectToSet = PRIMITIVES_TO_USE.get(type); if (objectToSet == null) { LOGGER.warn("No primitive to use for {}", type); } } // si c'est une interface, on utilise une implémentation par défaut else if (type.isInterface()) { Class <?> typeImp = IMPLEMENTATIONS_TO_USE.get(type); if (typeImp == null) { LOGGER.warn("No implementation defined for {}", type); } objectToSet = typeImp.newInstance(); } // sinon, on invoke le constructeur par défaut else { objectToSet = tryToInstantiateClass(type); } // pire cas de figure : whitebox if (objectToSet == null) { objectToSet = Whitebox.newInstance(type); } } catch (IllegalArgumentException e) { LOGGER.warn(e.getMessage(), e); } catch (InstantiationException e) { LOGGER.warn(e.getMessage(), e); } catch (IllegalAccessException e) { LOGGER.warn(e.getMessage(), e); } return objectToSet; } private Object tryToInstantiateClass(Class <?> type) { Object instance = null; Constructor <?>[] constructors = type.getDeclaredConstructors(); // Test every constructor for (Constructor <?> constructor : constructors) { constructor.setAccessible(true); Class <?>[] paramTypes = constructor.getParameterTypes(); try { // Constructor w/ params List <Object> params = new ArrayList <Object>(); for (Class <?> paramType : paramTypes) { params.add(paramType.getDeclaredConstructor().newInstance()); } instance = constructor.newInstance(params.toArray()); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) { LOGGER.debug("Could not instantiate {} with {} ", type.getName(), constructor.getName()); } } return instance; } /** * Type est un type primitif * * @param type * le type * @return Type est un type primitif */ private boolean typeIsBoxedPrimitive(Class <?> type) { return BOXED_PRIMITIVES.contains(type); } /** * Trouve le setter pour un attribut d'une entitée * * @param entity * la classe de l'entité * @param field * l'attribut * @return le setter ou null si non trouvé */ private Method findSetter(Class <?> entity, Field field) { for (String setterPrefix : POSSIBLE_SETTER_PREFIXES) { try { String setterToTry = setterPrefix + capitalizeFirstLetter(field.getName()); LOGGER.debug("Trying setter {}", setterToTry); Method setter = entity.getMethod(setterToTry, field.getType()); return setter; } catch (SecurityException e) { LOGGER.warn("Security exception for class {}", entity.getName()); } catch (NoSuchMethodException e) { LOGGER.debug("No setter found"); } } return null; } /** * Renvoie la string avec la première lettre en majuscule * * @param str * la string * @return string avec la première lettre en majuscule */ private String capitalizeFirstLetter(String str) { if (str.length() == 0) { return str; } else if (str.length() == 1) { return String.valueOf(Character.toUpperCase(str.charAt(0))); } return Character.toUpperCase(str.charAt(0)) + str.substring(1); } /** * Trouve le getter pour un attribut d'une entitée * * @param entity * la classe de l'entité * @param field * l'attribut * @return le getter ou null si non trouvé */ private Method findGetter(Class <?> entity, Field field) { for (String getterPrefix : POSSIBLE_GETTER_PREFIXES) { try { String getterToTry = getterPrefix + capitalizeFirstLetter(field.getName()); LOGGER.debug("Trying getter {}", getterToTry); Method getter = entity.getMethod(getterToTry); return getter; } catch (SecurityException e) { LOGGER.warn("Security exception for class {}", entity.getName()); } catch (NoSuchMethodException e) { LOGGER.debug("No setter found"); } } return null; } /** * Collecte toutes les entités du package * * @param pkg * le pkg de domain * @return les entités collectées */ private List <Class <?>> collectAllEntities(String pkg, TypeTest typeTest) { List <Class <?>> entitiesToTest = new ArrayList <Class <?>>(); try { List <Class <?>> classes = getClasses(pkg); for (Class <?> potentialEntity : classes) { LOGGER.debug("Potential entity : " + potentialEntity.getName()); if (TypeTest.Entities.equals(typeTest) && potentialEntity.getAnnotation(Entity.class) != null) { LOGGER.debug(" ->>>> this is an Entity"); entitiesToTest.add(potentialEntity); } else if (TypeTest.Representations.equals(typeTest) && potentialEntity.getSimpleName().endsWith( "Representation")) { LOGGER.debug(" ->>>> this is a Representation"); entitiesToTest.add(potentialEntity); } else if (TypeTest.Dto.equals(typeTest) && potentialEntity.getSimpleName().endsWith("Dto")) { LOGGER.debug(" ->>>> this is a Dto"); entitiesToTest.add(potentialEntity); } else if (TypeTest.Vo.equals(typeTest) && potentialEntity.getAnnotation(Embeddable.class) != null) { LOGGER.debug(" ->>>> this is a Vo"); entitiesToTest.add(potentialEntity); } } } catch (ClassNotFoundException | IOException e) { Assert.fail("Unable to scan package " + pkg); LOGGER.error("Unable to scan package.", e); } return entitiesToTest; } private static List <Class <?>> getClasses(String packageName) throws ClassNotFoundException, IOException { ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); Assert.assertNotNull(classLoader); String path = packageName.replace('.', '/'); Enumeration <URL> resources = classLoader.getResources(path); List <File> dirs = new ArrayList <File>(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); dirs.add(new File(resource.getFile())); } ArrayList <Class <?>> classes = new ArrayList <Class <?>>(); for (File directory : dirs) { classes.addAll(findClasses(directory, packageName)); } return classes; } /** * Recursive method used to find all classes in a given directory and * subdirs. * * @param directory * The base directory * @param packageName * The package name for classes found inside the base directory * @return The classes * @throws ClassNotFoundException */ private static List <Class <?>> findClasses(File directory, String packageName) throws ClassNotFoundException { List <Class <?>> classes = new ArrayList <Class <?>>(); if (!directory.exists()) { return classes; } File[] files = directory.listFiles(); for (File file : files) { if (file.isDirectory()) { Assert.assertFalse(file.getName().contains(".")); classes.addAll(findClasses(file, packageName + "." + file.getName())); } else if (file.getName().endsWith(".class")) { try { classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); } catch (Throwable e) { LOGGER.warn("Could not initialize {}", file.getName()); } } } return classes; } }
No hay comentarios:
Publicar un comentario