Malditas variables estáticas

En una aplicación más o menos grande y seria de java es normal leer algún fichero con propiedades de configuración para nuestro programa. Esas propiedades suelen ser múltiples y variadas, para módulos de todos los niveles que no tienen nada que ver unos con otros.

Una opción es leer ese fichero de propiedades desde un sitio cercano al main() de nuestra aplicación e ir pasando estas propiedades a los distintos módulos. Si hay módulos en varios niveles, estos deberán ir pasándose las propiedades de unos a otros, ya que el main() posiblemente no pueda acceder directamente a los módulos de bajo nivel.

Para evitar este trasiego de propiedades por todo el código, tiendo a hacer un código java de este estilo

public class FicheroPropiedades {

   private static String pathFicheroPropiedades = "path/fichero.properties";

   private static Properties propiedades = null;

   static {
      propiedades = cargaFichero();
   }

   public static String getProperty (String key) {
      return properties.getProperty(key);
   }
}

Es decir, una clase con todo estático a la que pedir los valores de las propiedades y un inicializador estático de forma que el fichero de propiedades se cargará en cuanto alguien "mencione" esta clase.

Pues bien, esto son todo problemas para los test de JUnit.

Por un lado, posiblemente tengamos o queramos un fichero de propiedades específico para nuestros test. O queramos cambiar alguna propiedad en algún test. Al leer nuestro código bajo test las propiedades de esta clase, es necesario hacerle cosas a esta clase para poder modificarla a gusto. Por ejemplo, poner public el path del fichero para poder cargar un fichero distinto en los test, o poner un cargaFichero(path), o añadir un setProperty() para modificar alguna propiedad concreta, o cualquier otra modificación que se os ocurra. Pero eso no es bonito. No es bonito tener que modificar una clase de nuestro código real para poder manejarla desde los test.

Por otro lado, si tenemos alguna herramienta que ejecuta automáticamente todos los test, es posible que esta clase sólo se inicialice en la ejecución del primer test, manteniendo sus valores para el resto de test. O que al pasar algún test que modifica propiedades, las deje modificadas para el siguiente. En fin, que podemos tener problemas en función de qué test se ejecuten primero, o si añadimos un test nuevo que modifique estas propiedades, puede alterar el resultado de los que van detrás.

Este es otro motivo, además de la mantenibilidad del código, para no usar/acceder a atributos estáticos desde todo el código  si no es realmente necesario. Los test de JUnit pueden hacerse dependientes del orden en que se ejecutan, ya que unos pueden cambiar o no los valores de estas variables estáticas según sus necesidades, incordiando a los que vienen detrás.

Aunque sea un poco rollo a la hora de codificar, suele ser mejor pasar las cosas a cada módulo a traves de métodos set() o en el constructor, aunque sea todo de golpe con un setProperties(Properties), que andar inicializando automáticamente variables estáticas y acceder a ellas desde todos sitios.

Esta entrada ha sido publicada en java y etiquetada como , . Guarda el enlace permanente.

4 respuestas a Malditas variables estáticas

  1. blaxter dijo:

    Una variable estática es una variable global. La mayor fuente de errores y problemas, con permiso de… de nadie en realidad.

  2. Esteban dijo:

    Esta claro que las variables estaticas son fuente de problemas. Pero para el problema concreto del que hablas, para poder variar su valor en los test, se puede haer utilizando jmockit. Echale un vistazo porque es una muy buena libreria para resolver dependencias con mocks de objetos

  3. David dijo:

    Yo suelo usar la configuración maven para test y main, con lo cual tengo dos juegos de properties.
    Para no ir pasándome el properties por el código, suelo crearme una clase singleton para acceder a las properties. A veces incluso hago una clase final con constantes que lo que hacen es acceder al properties mediante el singleton
    No es una solución que cubra todos los casos, pero suele darme buen resultado
    Para acceder a una propiedad hago:

    public final class Consts {
    //Constructor privado para evitar la instanciaci�n.
    private Consts() {}
    public static final int ROUTES_BUS = ConfigurationService.getInstance().getConfiguration().getInt(«routes.bus»);
    }
    La clase para la configuración:
    /*
    * Created on 09-abr-2009
    *
    * To change the template for this generated file go to
    * Window>Preferences>Java>Code Generation>Code and Comments
    */
    package cat.tmb.services.configuration;

    import java.net.URL;

    import org.apache.commons.configuration.PropertiesConfiguration;
    import org.apache.log4j.Logger;

    public class ConfigurationService {

    private static final Logger log = Logger.getLogger(ConfigurationService.class.getName());

    private static ConfigurationService instance = null;
    private PropertiesConfiguration configuration = null;
    private static final String defaultConfigFileAbsolutePath = «/googletransit/googletransit.properties»;

    protected ConfigurationService(){
    try {
    log.debug(«configuracion default»);
    configureServiceDefault();
    } catch (Exception e) {
    log.fatal(«No s’ha pogut configurar el servei de Configuració. LA APLICACIÓ NO ARRANCARÀ»);
    }
    }

    public void configureServiceDefault() throws Exception{
    configureService(defaultConfigFileAbsolutePath);
    }

    public void configureService(String configurationFile) throws Exception{
    try {

    PropertiesConfiguration config = null;
    /*
    *
    * */
    log.debug(«Configurant fitxer default: «+defaultConfigFileAbsolutePath);
    URL url = ConfigurationService.class.getResource(defaultConfigFileAbsolutePath);
    config = new PropertiesConfiguration(url);
    setConfiguration(config);

    } catch (Exception e) {
    log.fatal(«Error al configurar el servei de configuració inicial: «, e);
    throw e;
    }
    }

    public static ConfigurationService getInstance() {
    if (instance == null){
    instance = new ConfigurationService();
    }
    return instance;
    }

    public PropertiesConfiguration getConfiguration() {
    return this.configuration;
    }

    public void setConfiguration(PropertiesConfiguration configuration) {
    this.configuration = configuration;
    }

    }

  4. Chuidiang dijo:

    Sí, yo también tengo dos properties en maven, el de test y el del main. El problema surge cuando varios test necesitan propiedades distintas. maven ejecuta todos los test con la misma máquina virtual, por lo que estos valores estáticos se mantienen de un test a otro. O en cada test te aseguras de inicializar todo correctamente, o puedes tener efectos secundarios indeseados.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.