I have a code that is based on a configuration file called config.py which defines a class called Config and contains all the configuration options. As the config file can be located anywhere in the user's storage, so I use importlib.util to import it (as specified in this answer). I want to test this functionality with unittest for different configurations. How do I do it? A simple answer could be make a different file for every possible config I want to test and then pass its path to the config loader but this is not what I want. What I basically need is that I implement the Config class, and fake it as if it were the actual config file. How to achieve this?
EDIT Here is the code I want to test:
import os
import re
import traceback
import importlib.util
from typing import Any
from blessings import Terminal
term = Terminal()
class UnknownOption(Exception):
    pass
class MissingOption(Exception):
    pass
def perform_checks(config: Any):
    checklist = {
        "required": {
            "root": [
                "flask",
                "react",
                "mysql",
                "MODE",
                "RUN_REACT_IN_DEVELOPMENT",
                "RUN_FLASK_IN_DEVELOPMENT",
            ],
            "flask": ["HOST", "PORT", "config"],
             # More options
        },
        "optional": {
            "react": [
                "HTTPS",
                # More options
            ],
            "mysql": ["AUTH_PLUGIN"],
        },
    }
    # Check for missing required options
    for kind in checklist["required"]:
        prop = config if kind == "root" else getattr(config, kind)
        for val in kind:
            if not hasattr(prop, val):
                raise MissingOption(
                    "Error while parsing config: "
                    + f"{prop}.{val} is a required config "
                    + "option but is not specified in the configuration file."
                )
    def unknown_option(option: str):
        raise UnknownOption(
            "Error while parsing config: Found an unknown option: " + option
        )
    # Check for unknown options
    for val in vars(config):
        if not re.match("__[a-zA-Z0-9_]*__", val) and not callable(val):
            if val in checklist["optional"]:
                for ch_val in vars(val):
                    if not re.match("__[a-zA-Z0-9_]*__", ch_val) and not callable(
                        ch_val
                    ):
                        if ch_val not in checklist["optional"][val]:
                            unknown_option(f"Config.{val}.{ch_val}")
            else:
                unknown_option(f"Config.{val}")
    # Check for illegal options
    if config.react.HTTPS == "true":
        # HTTPS was set to true but no cert file was specified
        if not hasattr(config.react, "SSL_KEY_FILE") or not hasattr(
            config.react, "SSL_CRT_FILE"
        ):
            raise MissingOption(
                "config.react.HTTPS was set to True without specifying a key file and a crt file, which is illegal"
            )
        else:
            # Files were specified but are non-existent
            if not os.path.exists(config.react.SSL_KEY_FILE):
                raise FileNotFoundError(
                    f"The file at { config.react.SSL_KEY_FILE } was set as the key file"
                    + "in configuration but was not found."
                )
            if not os.path.exists(config.react.SSL_CRT_FILE):
                raise FileNotFoundError(
                    f"The file at { config.react.SSL_CRT_FILE } was set as the certificate file"
                    + "in configuration but was not found."
                )
def load_from_pyfile(root: str = None):
    """
    This loads the configuration from a `config.py` file located in the project root
    """
    PROJECT_ROOT = root or os.path.abspath(
        ".." if os.path.abspath(".").split("/")[-1] == "lib" else "."
    )
    config_file = os.path.join(PROJECT_ROOT, "config.py")
    print(f"Loading config from {term.green(config_file)}")
    # Load the config file
    spec = importlib.util.spec_from_file_location("", config_file)
    config = importlib.util.module_from_spec(spec)
    # Execute the script
    spec.loader.exec_module(config)
    # Not needed anymore
    del spec, config_file
    # Load the mode from environment variable and
    # if it is not specified use development mode
    MODE = int(os.environ.get("PROJECT_MODE", -1))
    conf: Any
    try:
        conf = config.Config()
        conf.load(PROJECT_ROOT, MODE)
    except Exception:
        print(term.red("Fatal: There was an error while parsing the config.py file:"))
        traceback.print_exc()
        print("This error is non-recoverable. Aborting...")
        exit(1)
    print("Validating configuration...")
    perform_checks(conf)
    print(
        "Configuration",
        term.green("OK"),
    )
 
    