En el siguiente código, me gustaría probar que si config_dir no existe, se crea.

    def __init__(
        self, config_dir: pathlib.Path = pathlib.Path().home() / ".config" / "moe",
    ):
        """Read configuration.

        Args:
            config_dir: Path of the configuration directory.
        """
        self.config_dir = config_dir

        if not self.config_dir.exists():
            self.config_dir.mkdir(parents=True)

        self.db_path: pathlib.Path = self.config_dir / "library.db"

        # initialize db
        engine = sqlalchemy.create_engine("sqlite:///" + str(self.db_path))
        library.Session.configure(bind=engine)
        library.Base.metadata.create_all(engine)  # create tables if they don't exist

Sin embargo, parece que no puedo averiguar cómo probar esa parte del código. He intentado

    def test_config_dir_dne(self, mocker):
        """We should create the config directory if it doesn't exist."""
        fake_path = mocker.Mock()
        moe.config.Config(fake_path)

        fake_path.mkdir.assert_called_once_with(parents=True)

Pero, a pathlib no le gusta que use el operador de combinación específico con el simulacro.

>       self.db_path: pathlib.Path = self.config_dir / "library.db"
E       TypeError: unsupported operand type(s) for /: 'Mock' and 'str'

Además, realmente no me importa esa parte del código para esta prueba, ni deseo crear el motor al final.

¿Cómo puedo comprobar si se llama a mkdirs en mi fake_path sin ejecutar ningún otro código?

0
Jacob Pavlock 10 ago. 2020 a las 00:11

2 respuestas

La mejor respuesta

Aquí hay tres enfoques generales:

  1. Burlarse más. Específicamente, puede parchear pathlib.Path y sqlalchemy.create_engine y todo lo demás que no desee probar.

  2. Utilice la inyección de dependencia para que pueda simular sin parchear. Haga que el constructor tome interfaces abstractas y luego haga que su prueba pase en versiones ficticias de ellas.

  3. Reestructura tu código para que cada unidad que quieras probar por sí sola tenga su propia función. Tome la lógica que desea probar (la creación del directorio de configuración), póngala en su propia función y luego pruebe unitariamente esa función independientemente de su constructor.

2
Samwise 9 ago. 2020 a las 21:19

Agregue un argumento adicional a __init__, la función utilizada para inicializar la base de datos. De forma predeterminada, utilizará una función proporcionada por la propia clase.

def __init__(
    self,
    config_dir: pathlib.Path = pathlib.Path().home() / ".config" / "moe",
    db_initializer=None
):
    """Read configuration.

    Args:
        config_dir: Path of the configuration directory.
    """
    self.config_dir = config_dir

    if not self.config_dir.exists():
        self.config_dir.mkdir(parents=True)

    self.db_path: pathlib.Path = self.config_dir / "library.db"

    if db_initializer is None:
        db_intializer = self._init_db

    db_initializer(self.dp_path)

@staticmethod
def _init_db(p):
    engine = sqlalchemy.create_engine("sqlite:///" + str(p))
    library.Session.configure(bind=engine)
    library.Base.metadata.create_all(engine)

Cuando pruebe la función, pasará una función de no hacer nada.

def test_config_dir_dne(self, mocker):
        """We should create the config directory if it doesn't exist."""
        fake_path = mocker.Mock()
        moe.config.Config(fake_path, lambda p: None)

        fake_path.mkdir.assert_called_once_with(parents=True)
0
chepner 9 ago. 2020 a las 21:44