Repository: ESMValGroup/ESMValCore

Issue:
CMOR checks are done twice or even more often
#### Fix data

During loading a dataset, the steps `fix_data` and `cmor_check_data` are performed right after each other:

https://github.com/ESMValGroup/ESMValCore/blob/f00b6c6190a114ffe12aea007bd31b066d2cb8d9/esmvalcore/dataset.py#L756-L767

However, `fix_data` already includes a CMOR check:

https://github.com/ESMValGroup/ESMValCore/blob/f00b6c6190a114ffe12aea007bd31b066d2cb8d9/esmvalcore/cmor/fix.py#L270-L277

This is basically identical to what is done in `cmor_check_data`:

https://github.com/ESMValGroup/ESMValCore/blob/f00b6c6190a114ffe12aea007bd31b066d2cb8d9/esmvalcore/cmor/check.py#L1167-L1173

Note that the `fail_on_error=False` flag is kind of misleading, as it simply means ["collect all messages before reporting/failing"](https://github.com/ESMValGroup/ESMValCore/blob/f00b6c6190a114ffe12aea007bd31b066d2cb8d9/esmvalcore/cmor/check.py#L98-L100) instead of reporting them immediately. If there is an error, an exception will be raised regardless of the value of the flag.

#### Fix metadata

The situation for `fix_metadata` is quite similar. The only difference is that there is an [additional `concatenate` step ](https://github.com/ESMValGroup/ESMValCore/blob/f00b6c6190a114ffe12aea007bd31b066d2cb8d9/esmvalcore/dataset.py#L739-L751)in between `fix_metadata` and `cmor_check_metadata`.

#### What to do?

Doing the exact same CMOR check twice (or even more often, as `fix_metadata` does the check for each input file prior to concatenation, which can be hundreds or thousands of files **(!)**) is not really efficient.

My suggestion would be:

1. Make the CMOR check in `fix_metadata` and `fix_data` optional (to maintain backwards-compatibility). If not used, add the corresponding automatic fixes to these functions. This is especially necessary for `fix_metadata`, since the subsequent concatenation might fail without the automatic fixes. In addition, it is conceptually sensible to add the fixes to the actual fix preprocessors.
2. Disable the CMOR check in `fix_metadata` and `fix_data` in [`Dataset._load`](https://github.com/ESMValGroup/ESMValCore/blob/f00b6c6190a114ffe12aea007bd31b066d2cb8d9/esmvalcore/dataset.py#L707).
3. Add an optional flag to `cmor_check_data` and `cmor_check_metadata` for `fail_on_error` and use it in [`Dataset._load`](https://github.com/ESMValGroup/ESMValCore/blob/f00b6c6190a114ffe12aea007bd31b066d2cb8d9/esmvalcore/dataset.py#L707).

This would result in one CMOR check (data and metadata) per dataset instead of one metadata check per input file (which, as mentioned, can be thousands) and be fully backwards compatible. For models like ICON (which stores one file per time step), this should be MUCH faster when analyzing sub-daily data.

@ESMValGroup/technical-lead-development-team any opinions on this? Did I miss any reasons which would require us doing these CMOR checks multiple times?


Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2157?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup) Report
> Merging [#2157](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2157?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup) (60ad9d5) into [main](https://app.codecov.io/gh/ESMValGroup/ESMValCore/commit/f00b6c6190a114ffe12aea007bd31b066d2cb8d9?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup) (f00b6c6) will **increase** coverage by `0.00%`.
> The diff coverage is `91.66%`.

```diff
@@           Coverage Diff           @@
##             main    #2157   +/-   ##
=======================================
  Coverage   93.09%   93.09%           
=======================================
  Files         237      237           
  Lines       12795    12800    +5     
=======================================
+ Hits        11911    11916    +5     
  Misses        884      884           
```


| [Files Changed](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2157?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup) | Coverage Δ | |
|---|---|---|
| [esmvalcore/dataset.py](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2157?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup#diff-ZXNtdmFsY29yZS9kYXRhc2V0LnB5) | `100.00% <ø> (ø)` | |
| [esmvalcore/cmor/check.py](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2157?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup#diff-ZXNtdmFsY29yZS9jbW9yL2NoZWNrLnB5) | `97.62% <86.66%> (+0.01%)` | :arrow_up: |
| [esmvalcore/cmor/fix.py](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2157?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup#diff-ZXNtdmFsY29yZS9jbW9yL2ZpeC5weQ==) | `100.00% <100.00%> (ø)` | |


schlunma: @ESMValGroup/technical-lead-development-team this is ready for review now.

As already announced in https://github.com/ESMValGroup/ESMValCore/issues/2154 this has become a massive PR now, but I think it's worth it. The separation of fixes and CMOR checks is much clearer now, and after removing the checks from the fixes in v2.12 this will lead to much faster run times for ICON-related recipes.

I tested this extensively with many recipes, but didn't find any issues with it. Apart from the deprecations, all changes here are fully backwards-compatible. The output from running the recipes is exactly identical to the current main branch.

Thanks, and sorry again for this massive PR. 🍻 
schlunma: Thanks @bouweandela for the review! :rocket: Apart from renaming `AutomaticFix` I think I addressed all your comments. Please let me know what you think.
bouweandela: Thanks for making the changes!
bouweandela: > An easy way to fix this is to build the _get_single_cube function into AutomaticFix.fix_metadata.

Sounds fine to me, that is quite similar to the current implementation
bouweandela: Thanks @schlunma, LGTM!
valeriupredoi: Superb work, gents - I can merge now if you guys ready-ready 🍺 

Patch:
diff --git a/esmvalcore/cmor/_fixes/cmip6/iitm_esm.py b/esmvalcore/cmor/_fixes/cmip6/iitm_esm.py
index 6ee5833c2..a6dee710c 100644
--- a/esmvalcore/cmor/_fixes/cmip6/iitm_esm.py
+++ b/esmvalcore/cmor/_fixes/cmip6/iitm_esm.py
@@ -3,7 +3,7 @@ import logging
 
 import numpy as np
 
-from esmvalcore.cmor.check import _get_time_bounds
+from esmvalcore.cmor._fixes.fix import get_time_bounds
 
 from ..common import OceanFixGrid
 from ..fix import Fix
@@ -33,7 +33,7 @@ class AllVars(Fix):
         for cube in cubes:
             freq = self.extra_facets["frequency"]
             time = cube.coord("time", dim_coords=True)
-            bounds = _get_time_bounds(time, freq)
+            bounds = get_time_bounds(time, freq)
             if np.any(bounds != time.bounds):
                 time.bounds = bounds
         logger.warning(
diff --git a/esmvalcore/cmor/_fixes/cmip6/kace_1_0_g.py b/esmvalcore/cmor/_fixes/cmip6/kace_1_0_g.py
index f2a0a1b7a..5e27377b0 100644
--- a/esmvalcore/cmor/_fixes/cmip6/kace_1_0_g.py
+++ b/esmvalcore/cmor/_fixes/cmip6/kace_1_0_g.py
@@ -3,7 +3,7 @@ import logging
 
 import numpy as np
 
-from esmvalcore.cmor.check import _get_time_bounds
+from esmvalcore.cmor._fixes.fix import get_time_bounds
 
 from ..common import ClFixHybridHeightCoord, OceanFixGrid
 from ..fix import Fix
@@ -39,7 +39,7 @@ class AllVars(Fix):
         for cube in cubes:
             freq = self.extra_facets["frequency"]
             time = cube.coord("time", dim_coords=True)
-            bounds = _get_time_bounds(time, freq)
+            bounds = get_time_bounds(time, freq)
             if np.any(bounds != time.bounds):
                 time.bounds = bounds
         logger.warning(
diff --git a/esmvalcore/cmor/_fixes/fix.py b/esmvalcore/cmor/_fixes/fix.py
index 2deeb0d6d..4b5163d4a 100644
--- a/esmvalcore/cmor/_fixes/fix.py
+++ b/esmvalcore/cmor/_fixes/fix.py
@@ -3,17 +3,36 @@ from __future__ import annotations
 
 import importlib
 import inspect
+import logging
 import tempfile
+from collections.abc import Sequence
+from datetime import datetime
 from pathlib import Path
 from typing import TYPE_CHECKING, Any, Optional
 
+import numpy as np
+from cf_units import Unit
+from iris.coords import Coord, CoordExtent
 from iris.cube import Cube, CubeList
-
-from ..table import CMOR_TABLES
+from iris.exceptions import UnitConversionError
+from iris.util import reverse
+
+from esmvalcore.cmor._utils import (
+    _get_alternative_generic_lev_coord,
+    _get_generic_lev_coord_names,
+    _get_new_generic_level_coord,
+    _get_simplified_calendar,
+    _get_single_cube,
+    _is_unstructured_grid,
+)
+from esmvalcore.cmor.table import get_var_info
+from esmvalcore.iris_helpers import date2num
 
 if TYPE_CHECKING:
-    from ...config import Session
-    from ..table import VariableInfo
+    from esmvalcore.cmor.table import CoordinateInfo, VariableInfo
+    from esmvalcore.config import Session
+
+logger = logging.getLogger(__name__)
 
 
 class Fix:
@@ -24,19 +43,23 @@ class Fix:
         vardef: VariableInfo,
         extra_facets: Optional[dict] = None,
         session: Optional[Session] = None,
+        frequency: Optional[str] = None,
     ) -> None:
         """Initialize fix object.
 
         Parameters
         ----------
         vardef:
-            CMOR table entry.
+            CMOR table entry of the variable.
         extra_facets:
             Extra facets are mainly used for data outside of the big projects
             like CMIP, CORDEX, obs4MIPs. For details, see :ref:`extra_facets`.
         session:
             Current session which includes configuration and directory
             information.
+        frequency:
+            Expected frequency of the variable. If not given, use the one from
+            the CMOR table entry of the variable.
 
         """
         self.vardef = vardef
@@ -44,6 +67,9 @@ class Fix:
             extra_facets = {}
         self.extra_facets = extra_facets
         self.session = session
+        if frequency is None and self.vardef is not None:
+            frequency = self.vardef.frequency
+        self.frequency = frequency
 
     def fix_file(
         self,
@@ -76,7 +102,7 @@ class Fix:
         """
         return filepath
 
-    def fix_metadata(self, cubes: CubeList) -> CubeList:
+    def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]:
         """Apply fixes to the metadata of the cube.
 
         Changes applied here must not require data loading.
@@ -90,7 +116,7 @@ class Fix:
 
         Returns
         -------
-        iris.cube.CubeList
+        Iterable[iris.cube.Cube]
             Fixed cubes. They can be different instances.
 
         """
@@ -163,6 +189,7 @@ class Fix:
         short_name: str,
         extra_facets: Optional[dict] = None,
         session: Optional[Session] = None,
+        frequency: Optional[str] = None,
     ) -> list:
         """Get the fixes that must be applied for a given dataset.
 
@@ -177,6 +204,8 @@ class Fix:
         before checking because it is not possible to use the character '-' in
         python names.
 
+        In addition, generic fixes for all datasets are added.
+
         Parameters
         ----------
         project:
@@ -193,6 +222,9 @@ class Fix:
         session:
             Current session which includes configuration and directory
             information.
+        frequency:
+            Expected frequency of the variable. If not given, use the one from
+            the CMOR table entry of the variable.
 
         Returns
         -------
@@ -200,8 +232,7 @@ class Fix:
             Fixes to apply for the given data.
 
         """
-        cmor_table = CMOR_TABLES[project]
-        vardef = cmor_table.get_variable(mip, short_name)
+        vardef = get_var_info(project, mip, short_name)
 
         project = project.replace('-', '_').lower()
         dataset = dataset.replace('-', '_').lower()
@@ -238,9 +269,24 @@ class Fix:
             )
             for fix_name in (short_name, mip.lower(), 'allvars'):
                 if fix_name in classes:
-                    fixes.append(classes[fix_name](
-                        vardef, extra_facets=extra_facets, session=session
-                    ))
+                    fixes.append(
+                        classes[fix_name](
+                            vardef,
+                            extra_facets=extra_facets,
+                            session=session,
+                            frequency=frequency,
+                        )
+                    )
+
+        # Always perform generic fixes for all datasets
+        fixes.append(
+            GenericFix(
+                vardef,  # type: ignore
+                extra_facets=extra_facets,
+                session=session,
+                frequency=frequency,
+            )
+        )
 
         return fixes
 
@@ -277,3 +323,661 @@ class Fix:
         else:
             output_dir.mkdir(parents=True, exist_ok=True)
         return output_dir / Path(filepath).name
+
+
+def get_next_month(month: int, year: int) -> tuple[int, int]:
+    """Get next month and year.
+
+    Parameters
+    ----------
+    month:
+        Current month.
+    year:
+        Current year.
+
+    Returns
+    -------
+    tuple[int, int]
+        Next month and next year.
+
+    """
+    if month != 12:
+        return month + 1, year
+    return 1, year + 1
+
+
+def get_time_bounds(time: Coord, freq: str):
+    """Get bounds for time coordinate.
+
+    Parameters
+    ----------
+    time:
+        Time coordinate.
+    freq:
+        Frequency.
+
+    Returns
+    -------
+    np.ndarray
+        Time bounds
+
+    Raises
+    ------
+    NotImplementedError
+        Non-supported frequency is given.
+
+    """
+    bounds = []
+    dates = time.units.num2date(time.points)
+    for step, date in enumerate(dates):
+        month = date.month
+        year = date.year
+        if freq in ['mon', 'mo']:
+            next_month, next_year = get_next_month(month, year)
+            min_bound = date2num(datetime(year, month, 1, 0, 0),
+                                 time.units, time.dtype)
+            max_bound = date2num(datetime(next_year, next_month, 1, 0, 0),
+                                 time.units, time.dtype)
+        elif freq == 'yr':
+            min_bound = date2num(datetime(year, 1, 1, 0, 0),
+                                 time.units, time.dtype)
+            max_bound = date2num(datetime(year + 1, 1, 1, 0, 0),
+                                 time.units, time.dtype)
+        elif freq == 'dec':
+            min_bound = date2num(datetime(year, 1, 1, 0, 0),
+                                 time.units, time.dtype)
+            max_bound = date2num(datetime(year + 10, 1, 1, 0, 0),
+                                 time.units, time.dtype)
+        else:
+            delta = {
+                'day': 12.0 / 24,
+                '6hr': 3.0 / 24,
+                '3hr': 1.5 / 24,
+                '1hr': 0.5 / 24,
+            }
+            if freq not in delta:
+                raise NotImplementedError(
+                    f"Cannot guess time bounds for frequency '{freq}'"
+                )
+            point = time.points[step]
+            min_bound = point - delta[freq]
+            max_bound = point + delta[freq]
+        bounds.append([min_bound, max_bound])
+
+    return np.array(bounds)
+
+
+class GenericFix(Fix):
+    """Class providing generic fixes for all datasets."""
+
+    def fix_metadata(self, cubes: Sequence[Cube]) -> CubeList:
+        """Fix cube metadata.
+
+        Parameters
+        ----------
+        cubes:
+            Cubes to be fixed.
+
+        Returns
+        -------
+        CubeList
+            Fixed cubes.
+
+        """
+        # Make sure the this fix also works when no extra_facets are given
+        if 'project' in self.extra_facets and 'dataset' in self.extra_facets:
+            dataset_str = (
+                f"{self.extra_facets['project']}:"
+                f"{self.extra_facets['dataset']}"
+            )
+        else:
+            dataset_str = None
+
+        # The following fixes are designed to operate on the actual cube that
+        # corresponds to the variable. Thus, it needs to be assured (possibly
+        # by prior dataset-specific fixes) that the cubes here contain only one
+        # relevant cube.
+        cube = _get_single_cube(
+            cubes, self.vardef.short_name, dataset_str=dataset_str
+        )
+
+        cube = self._fix_standard_name(cube)
+        cube = self._fix_long_name(cube)
+        cube = self._fix_psu_units(cube)
+        cube = self._fix_units(cube)
+
+        cube = self._fix_regular_coord_names(cube)
+        cube = self._fix_alternative_generic_level_coords(cube)
+        cube = self._fix_coords(cube)
+        cube = self._fix_time_coord(cube)
+
+        return CubeList([cube])
+
+    def fix_data(self, cube: Cube) -> Cube:
+        """Fix cube data.
+
+        Parameters
+        ----------
+        cube:
+            Cube to be fixed.
+
+        Returns
+        -------
+        Cube
+            Fixed cube.
+
+        """
+        return cube
+
+    @staticmethod
+    def _msg_suffix(cube: Cube) -> str:
+        """Get prefix for log messages."""
+        if 'source_file' in cube.attributes:
+            return f"\n(for file {cube.attributes['source_file']})"
+        return f"\n(for variable {cube.var_name})"
+
+    def _debug_msg(self, cube: Cube, msg: str, *args) -> None:
+        """Print debug message."""
+        msg += self._msg_suffix(cube)
+        logger.debug(msg, *args)
+
+    def _warning_msg(self, cube: Cube, msg: str, *args) -> None:
+        """Print debug message."""
+        msg += self._msg_suffix(cube)
+        logger.warning(msg, *args)
+
+    @staticmethod
+    def _set_range_in_0_360(array: np.ndarray) -> np.ndarray:
+        """Convert longitude coordinate to [0, 360]."""
+        return (array + 360.0) % 360.0
+
+    def _reverse_coord(self, cube: Cube, coord: Coord) -> tuple[Cube, Coord]:
+        """Reverse cube along a given coordinate."""
+        if coord.ndim == 1:
+            cube = reverse(cube, cube.coord_dims(coord))
+            coord = cube.coord(var_name=coord.var_name)
+            if coord.has_bounds():
+                bounds = coord.core_bounds()
+                right_bounds = bounds[:-2, 1]
+                left_bounds = bounds[1:-1, 0]
+                if np.all(right_bounds != left_bounds):
+                    coord.bounds = np.fliplr(bounds)
+            self._debug_msg(
+                cube,
+                "Coordinate %s values have been reversed",
+                coord.var_name,
+            )
+        return (cube, coord)
+
+    def _get_effective_units(self) -> str:
+        """Get effective units."""
+        if self.vardef.units.lower() == 'psu':
+            return '1'
+        return self.vardef.units
+
+    def _fix_units(self, cube: Cube) -> Cube:
+        """Fix cube units."""
+        if self.vardef.units:
+            units = self._get_effective_units()
+
+            # We use str(cube.units) in the following to catch `degrees` !=
+            # `degrees_north`
+            if str(cube.units) != units:
+                old_units = cube.units
+                try:
+                    cube.convert_units(units)
+                except (ValueError, UnitConversionError):
+                    self._warning_msg(
+                        cube,
+                        "Failed to convert cube units from '%s' to '%s'",
+                        old_units,
+                        units,
+                    )
+                else:
+                    self._warning_msg(
+                        cube,
+                        "Converted cube units from '%s' to '%s'",
+                        old_units,
+                        units,
+                    )
+        return cube
+
+    def _fix_standard_name(self, cube: Cube) -> Cube:
+        """Fix standard_name."""
+        # Do not change empty standard names
+        if not self.vardef.standard_name:
+            return cube
+
+        if cube.standard_name != self.vardef.standard_name:
+            self._warning_msg(
+                cube,
+                "Standard name changed from '%s' to '%s'",
+                cube.standard_name,
+                self.vardef.standard_name,
+            )
+            cube.standard_name = self.vardef.standard_name
+
+        return cube
+
+    def _fix_long_name(self, cube: Cube) -> Cube:
+        """Fix long_name."""
+        # Do not change empty long names
+        if not self.vardef.long_name:
+            return cube
+
+        if cube.long_name != self.vardef.long_name:
+            self._warning_msg(
+                cube,
+                "Long name changed from '%s' to '%s'",
+                cube.long_name,
+                self.vardef.long_name,
+            )
+            cube.long_name = self.vardef.long_name
+
+        return cube
+
+    def _fix_psu_units(self, cube: Cube) -> Cube:
+        """Fix psu units."""
+        if cube.attributes.get('invalid_units', '').lower() == 'psu':
+            cube.units = '1'
+            cube.attributes.pop('invalid_units')
+            self._debug_msg(cube, "Units converted from 'psu' to '1'")
+        return cube
+
+    def _fix_regular_coord_names(self, cube: Cube) -> Cube:
+        """Fix regular (non-generic-level) coordinate names."""
+        for cmor_coord in self.vardef.coordinates.values():
+            if cmor_coord.generic_level:
+                continue  # Ignore generic level coordinate in this function
+            if cube.coords(var_name=cmor_coord.out_name):
+                continue  # Coordinate found -> fine here
+            if cube.coords(cmor_coord.standard_name):
+                cube_coord = cube.coord(cmor_coord.standard_name)
+                self._fix_cmip6_multidim_lat_lon_coord(
+                    cube, cmor_coord, cube_coord
+                )
+        return cube
+
+    def _fix_alternative_generic_level_coords(self, cube: Cube) -> Cube:
+        """Fix alternative generic level coordinates."""
+        # Avoid overriding existing variable information
+        cmor_var_coordinates = self.vardef.coordinates.copy()
+        for (coord_name, cmor_coord) in cmor_var_coordinates.items():
+            if not cmor_coord.generic_level:
+                continue  # Ignore non-generic-level coordinates
+            if not cmor_coord.generic_lev_coords:
+                continue  # Cannot fix anything without coordinate info
+
+            # Extract names of the actual generic level coordinates present in
+            # the cube (e.g., `hybrid_height`, `standard_hybrid_sigma`)
+            (standard_name, out_name, name) = _get_generic_lev_coord_names(
+                cube, cmor_coord
+            )
+
+            # Make sure to update variable information with actual generic
+            # level coordinate if one has been found; this is necessary for
+            # subsequent fixes
+            if standard_name:
+                new_generic_level_coord = _get_new_generic_level_coord(
+                    self.vardef, cmor_coord, coord_name, name
+                )
+                self.vardef.coordinates[coord_name] = new_generic_level_coord
+                self._debug_msg(
+                    cube,
+                    "Generic level coordinate %s will be checked against %s "
+                    "coordinate information",
+                    coord_name,
+                    name,
+                )
+
+            # If a generic level coordinate has been found, we don't need to
+            # look for alternatives
+            if standard_name or out_name:
+                continue
+
+            # Search for alternative coordinates (i.e., regular level
+            # coordinates); if none found, do nothing
+            try:
+                (alternative_coord,
+                 cube_coord) = _get_alternative_generic_lev_coord(
+                    cube, coord_name, self.vardef.table_type
+                )
+            except ValueError:  # no alternatives found
+                continue
+
+            # Fix alternative coord
+            (cube, cube_coord) = self._fix_coord(
+                cube, alternative_coord, cube_coord
+            )
+
+        return cube
+
+    def _fix_cmip6_multidim_lat_lon_coord(
+        self,
+        cube: Cube,
+        cmor_coord: CoordinateInfo,
+        cube_coord: Coord,
+    ) -> None:
+        """Fix CMIP6 multidimensional latitude and longitude coordinates."""
+        is_cmip6_multidim_lat_lon = all([
+            'CMIP6' in self.vardef.table_type,
+            cube_coord.ndim > 1,
+            cube_coord.standard_name in ('latitude', 'longitude'),
+        ])
+        if is_cmip6_multidim_lat_lon:
+            self._debug_msg(
+                cube,
+                "Multidimensional %s coordinate is not set in CMOR standard, "
+                "ESMValTool will change the original value of '%s' to '%s' to "
+                "match the one-dimensional case",
+                cube_coord.standard_name,
+                cube_coord.var_name,
+                cmor_coord.out_name,
+            )
+            cube_coord.var_name = cmor_coord.out_name
+
+    def _fix_coord_units(
+        self,
+        cube: Cube,
+        cmor_coord: CoordinateInfo,
+        cube_coord: Coord,
+    ) -> None:
+        """Fix coordinate units."""
+        if not cmor_coord.units:
+            return
+
+        # We use str(cube_coord.units) in the following to catch `degrees` !=
+        # `degrees_north`
+        if str(cube_coord.units) != cmor_coord.units:
+            old_units = cube_coord.units
+            try:
+                cube_coord.convert_units(cmor_coord.units)
+            except (ValueError, UnitConversionError):
+                self._warning_msg(
+                    cube,
+                    "Failed to convert units of coordinate %s from '%s' to "
+                    "'%s'",
+                    cmor_coord.out_name,
+                    old_units,
+                    cmor_coord.units,
+                )
+            else:
+                self._warning_msg(
+                    cube,
+                    "Coordinate %s units '%s' converted to '%s'",
+                    cmor_coord.out_name,
+                    old_units,
+                    cmor_coord.units,
+                )
+
+    def _fix_requested_coord_values(
+        self,
+        cube: Cube,
+        cmor_coord: CoordinateInfo,
+        cube_coord: Coord,
+    ) -> None:
+        """Fix requested coordinate values."""
+        if not cmor_coord.requested:
+            return
+
+        # Cannot fix non-1D points
+        if cube_coord.core_points().ndim != 1:
+            return
+
+        # Get requested CMOR values
+        try:
+            cmor_points = np.array(cmor_coord.requested, dtype=float)
+        except ValueError:
+            return
+
+        # Align coordinate points with CMOR values if possible
+        if cube_coord.core_points().shape != cmor_points.shape:
+            return
+        atol = 1e-7 * np.mean(cmor_points)
+        align_coords = np.allclose(
+            cube_coord.core_points(),
+            cmor_points,
+            rtol=1e-7,
+            atol=atol,
+        )
+        if align_coords:
+            cube_coord.points = cmor_points
+            self._debug_msg(
+                cube,
+                "Aligned %s points with CMOR points",
+                cmor_coord.out_name,
+            )
+
+    def _fix_longitude_0_360(
+        self,
+        cube: Cube,
+        cmor_coord: CoordinateInfo,
+        cube_coord: Coord,
+    ) -> tuple[Cube, Coord]:
+        """Fix longitude coordinate to be in [0, 360]."""
+        if not cube_coord.standard_name == 'longitude':
+            return (cube, cube_coord)
+
+        # Only apply fixes when values are outside of valid range [0, 360]
+        inside_0_360 = all([
+            cube_coord.core_points().min() >= 0.0,
+            cube_coord.core_points().max() <= 360.0,
+        ])
+        if inside_0_360:
+            return (cube, cube_coord)
+
+        # Cannot fix longitudes outside [-360, 720]
+        if np.any(cube_coord.core_points() < -360.0):
+            return (cube, cube_coord)
+        if np.any(cube_coord.core_points() > 720.0):
+            return (cube, cube_coord)
+
+        # cube.intersection only works for cells with 0 or 2 bounds
+        # Note: nbounds==0 means there are no bounds given, nbounds==2
+        # implies a regular grid with bounds in the grid direction,
+        # nbounds>2 implies an irregular grid with bounds given as vertices
+        # of the cell polygon.
+        if cube_coord.ndim == 1 and cube_coord.nbounds in (0, 2):
+            lon_extent = CoordExtent(cube_coord, 0.0, 360., True, False)
+            cube = cube.intersection(lon_extent)
+        else:
+            new_lons = cube_coord.core_points().copy()
+            new_lons = self._set_range_in_0_360(new_lons)
+
+            if cube_coord.core_bounds() is None:
+                new_bounds = None
+            else:
+                new_bounds = cube_coord.core_bounds().copy()
+                new_bounds = self._set_range_in_0_360(new_bounds)
+
+            new_coord = cube_coord.copy(new_lons, new_bounds)
+            dims = cube.coord_dims(cube_coord)
+            cube.remove_coord(cube_coord)
+            cube.add_aux_coord(new_coord, dims)
+        new_coord = cube.coord(var_name=cmor_coord.out_name)
+        self._debug_msg(cube, "Shifted longitude to [0, 360]")
+
+        return (cube, new_coord)
+
+    def _fix_coord_bounds(
+        self,
+        cube: Cube,
+        cmor_coord: CoordinateInfo,
+        cube_coord: Coord,
+    ) -> None:
+        """Fix coordinate bounds."""
+        if cmor_coord.must_have_bounds != 'yes' or cube_coord.has_bounds():
+            return
+
+        # Skip guessing bounds for unstructured grids
+        if _is_unstructured_grid(cube) and cube_coord.standard_name in (
+                'latitude', 'longitude'):
+            self._debug_msg(
+                cube,
+                "Will not guess bounds for coordinate %s of unstructured grid",
+                cube_coord.var_name,
+            )
+            return
+
+        try:
+            cube_coord.guess_bounds()
+            self._warning_msg(
+                cube,
+                "Added guessed bounds to coordinate %s",
+                cube_coord.var_name,
+            )
+        except ValueError as exc:
+            self._warning_msg(
+                cube,
+                "Cannot guess bounds for coordinate %s: %s",
+                cube.var_name,
+                str(exc),
+            )
+
+    def _fix_coord_direction(
+        self,
+        cube: Cube,
+        cmor_coord: CoordinateInfo,
+        cube_coord: Coord,
+    ) -> tuple[Cube, Coord]:
+        """Fix coordinate direction (increasing vs. decreasing)."""
+        # Skip fix for a variety of reasons
+        if cube_coord.ndim > 1:
+            return (cube, cube_coord)
+        if cube_coord.dtype.kind == 'U':
+            return (cube, cube_coord)
+        if _is_unstructured_grid(cube) and cube_coord.standard_name in (
+                'latitude', 'longitude'
+        ):
+            return (cube, cube_coord)
+        if len(cube_coord.core_points()) == 1:
+            return (cube, cube_coord)
+        if not cmor_coord.stored_direction:
+            return (cube, cube_coord)
+
+        # Fix coordinates with wrong direction
+        if cmor_coord.stored_direction == 'increasing':
+            if cube_coord.core_points()[0] > cube_coord.core_points()[1]:
+                (cube, cube_coord) = self._reverse_coord(cube, cube_coord)
+        elif cmor_coord.stored_direction == 'decreasing':
+            if cube_coord.core_points()[0] < cube_coord.core_points()[1]:
+                (cube, cube_coord) = self._reverse_coord(cube, cube_coord)
+
+        return (cube, cube_coord)
+
+    def _fix_time_units(self, cube: Cube, cube_coord: Coord) -> None:
+        """Fix time units in cube and attributes."""
+        # Fix cube units
+        old_units = cube_coord.units
+        cube_coord.convert_units(
+            Unit(
+                'days since 1850-1-1 00:00:00',
+                calendar=cube_coord.units.calendar,
+            )
+        )
+        simplified_cal = _get_simplified_calendar(cube_coord.units.calendar)
+        cube_coord.units = Unit(
+            cube_coord.units.origin, calendar=simplified_cal
+        )
+
+        # Fix units of time-related cube attributes
+        attrs = cube.attributes
+        parent_time = 'parent_time_units'
+        if parent_time in attrs:
+            if attrs[parent_time] in 'no parent':
+                pass
+            else:
+                try:
+                    parent_units = Unit(attrs[parent_time], simplified_cal)
+                except ValueError:
+                    pass
+                else:
+                    attrs[parent_time] = 'days since 1850-1-1 00:00:00'
+
+                    branch_parent = 'branch_time_in_parent'
+                    if branch_parent in attrs:
+                        attrs[branch_parent] = parent_units.convert(
+                            attrs[branch_parent], cube_coord.units)
+
+                    branch_child = 'branch_time_in_child'
+                    if branch_child in attrs:
+                        attrs[branch_child] = old_units.convert(
+                            attrs[branch_child], cube_coord.units)
+
+    def _fix_time_bounds(self, cube: Cube, cube_coord: Coord) -> None:
+        """Fix time bounds."""
+        times = {'time', 'time1', 'time2', 'time3'}
+        key = times.intersection(self.vardef.coordinates)
+        cmor = self.vardef.coordinates[' '.join(key)]
+        if cmor.must_have_bounds == 'yes' and not cube_coord.has_bounds():
+            cube_coord.bounds = get_time_bounds(cube_coord, self.frequency)
+            self._warning_msg(
+                cube,
+                "Added guessed bounds to coordinate %s",
+                cube_coord.var_name,
+            )
+
+    def _fix_time_coord(self, cube: Cube) -> Cube:
+        """Fix time coordinate."""
+        # Make sure to get dimensional time coordinate if possible
+        if cube.coords('time', dim_coords=True):
+            cube_coord = cube.coord('time', dim_coords=True)
+        elif cube.coords('time'):
+            cube_coord = cube.coord('time')
+        else:
+            return cube
+
+        # Cannot fix wrong time that are not references
+        if not cube_coord.units.is_time_reference():
+            return cube
+
+        # Fix time units
+        self._fix_time_units(cube, cube_coord)
+
+        # Remove time_origin from coordinate attributes
+        cube_coord.attributes.pop('time_origin', None)
+
+        # Fix time bounds
+        self._fix_time_bounds(cube, cube_coord)
+
+        return cube
+
+    def _fix_coord(
+        self,
+        cube: Cube,
+        cmor_coord: CoordinateInfo,
+        cube_coord: Coord,
+    ) -> tuple[Cube, Coord]:
+        """Fix non-time coordinate."""
+        self._fix_coord_units(cube, cmor_coord, cube_coord)
+        (cube, cube_coord) = self._fix_longitude_0_360(
+            cube, cmor_coord, cube_coord
+        )
+        self._fix_coord_bounds(cube, cmor_coord, cube_coord)
+        (cube, cube_coord) = self._fix_coord_direction(
+            cube, cmor_coord, cube_coord
+        )
+        self._fix_requested_coord_values(cube, cmor_coord, cube_coord)
+        return (cube, cube_coord)
+
+    def _fix_coords(self, cube: Cube) -> Cube:
+        """Fix non-time coordinates."""
+        for cmor_coord in self.vardef.coordinates.values():
+
+            # Cannot fix generic level coords with no unique CMOR information
+            if cmor_coord.generic_level and not cmor_coord.out_name:
+                continue
+
+            # Try to get coordinate from cube; if it does not exists, skip
+            if not cube.coords(var_name=cmor_coord.out_name):
+                continue
+            cube_coord = cube.coord(var_name=cmor_coord.out_name)
+
+            # Fixes for time coord are done separately
+            if cube_coord.var_name == 'time':
+                continue
+
+            # Fixes
+            (cube, cube_coord) = self._fix_coord(cube, cmor_coord, cube_coord)
+
+        return cube
diff --git a/esmvalcore/cmor/_utils.py b/esmvalcore/cmor/_utils.py
new file mode 100644
index 000000000..4b083f19d
--- /dev/null
+++ b/esmvalcore/cmor/_utils.py
@@ -0,0 +1,219 @@
+"""Utilities for CMOR module."""
+from __future__ import annotations
+
+import logging
+from collections.abc import Sequence
+from typing import Optional
+
+from iris.coords import Coord
+from iris.cube import Cube
+from iris.exceptions import CoordinateNotFoundError
+
+from esmvalcore.cmor.table import CMOR_TABLES, CoordinateInfo, VariableInfo
+
+logger = logging.getLogger(__name__)
+
+_ALTERNATIVE_GENERIC_LEV_COORDS = {
+    'alevel': {
+        'CMIP5': ['alt40', 'plevs'],
+        'CMIP6': ['alt16', 'plev3'],
+        'obs4MIPs': ['alt16', 'plev3'],
+    },
+    'zlevel': {
+        'CMIP3': ['pressure'],
+    },
+}
+
+
+def _get_alternative_generic_lev_coord(
+    cube: Cube,
+    coord_name: str,
+    cmor_table_type: str,
+) -> tuple[CoordinateInfo, Coord]:
+    """Find alternative generic level coordinate in cube.
+
+    Parameters
+    ----------
+    cube:
+        Cube to be checked.
+    coord_name:
+        Name of the generic level coordinate.
+    cmor_table_type:
+        CMOR table type, e.g., CMIP3, CMIP5, CMIP6. Note: This is NOT the
+        project of the dataset, but rather the entry `cmor_type` in
+        `config-developer.yml`.
+
+    Returns
+    -------
+    tuple[CoordinateInfo, Coord]
+        Coordinate information from the CMOR tables and the corresponding
+        coordinate in the cube.
+
+    Raises
+    ------
+    ValueError
+        No valid alternative generic level coordinate present in cube.
+
+    """
+    alternatives_for_coord = _ALTERNATIVE_GENERIC_LEV_COORDS.get(
+        coord_name, {}
+    )
+    allowed_alternatives = alternatives_for_coord.get(cmor_table_type, [])
+
+    # Check if any of the allowed alternative coordinates is present in the
+    # cube
+    for allowed_alternative in allowed_alternatives:
+        cmor_coord = CMOR_TABLES[cmor_table_type].coords[allowed_alternative]
+        if cube.coords(var_name=cmor_coord.out_name):
+            cube_coord = cube.coord(var_name=cmor_coord.out_name)
+            return (cmor_coord, cube_coord)
+
+    raise ValueError(
+        f"Found no valid alternative coordinate for generic level coordinate "
+        f"'{coord_name}'"
+    )
+
+
+def _get_generic_lev_coord_names(
+    cube: Cube,
+    cmor_coord: CoordinateInfo,
+) -> tuple[str | None, str | None, str | None]:
+    """Try to get names of a generic level coordinate.
+
+    Parameters
+    ----------
+    cube:
+        Cube to be checked.
+    cmor_coord:
+        Coordinate information from the CMOR table with a non-emmpty
+        `generic_lev_coords` :obj:`dict`.
+
+    Returns
+    -------
+    tuple[str | None, str | None, str | None]
+        Tuple of `standard_name`, `out_name`, and `name` of the generic level
+        coordinate present in the cube. Values are ``None`` if generic level
+        coordinate has not been found in cube.
+
+    """
+    standard_name = None
+    out_name = None
+    name = None
+
+    # Iterate over all possible generic level coordinates
+    for coord in cmor_coord.generic_lev_coords.values():
+        # First, try to use var_name to find coordinate
+        if cube.coords(var_name=coord.out_name):
+            cube_coord = cube.coord(var_name=coord.out_name)
+            out_name = coord.out_name
+            if cube_coord.standard_name == coord.standard_name:
+                standard_name = coord.standard_name
+                name = coord.name
+
+        # Second, try to use standard_name to find coordinate
+        elif cube.coords(coord.standard_name):
+            standard_name = coord.standard_name
+            name = coord.name
+
+    return (standard_name, out_name, name)
+
+
+def _get_new_generic_level_coord(
+    var_info: VariableInfo,
+    generic_level_coord: CoordinateInfo,
+    generic_level_coord_name: str,
+    new_coord_name: str | None,
+) -> CoordinateInfo:
+    """Get new generic level coordinate.
+
+    There are a variety of possible options for each generic level coordinate
+    (e.g., `alevel`) which is actually present in a cube, for example,
+    `hybrid_height` or `standard_hybrid_sigma`. This function returns the new
+    coordinate (e.g., `new_coord_name=hybrid_height`) with the relevant
+    metadata.
+
+    Note
+    ----
+    This alters the corresponding entry of the original generic level
+    coordinate's `generic_level_coords` attribute (i.e.,
+    ``generic_level_coord.generic_level_coords[new_coord_name]`) in-place!
+
+    Parameters
+    ----------
+    var_info:
+        CMOR variable information.
+    generic_level_coord:
+        Original generic level coordinate.
+    generic_level_coord_name:
+        Original name of the generic level coordinate (e.g., `alevel`).
+    new_coord_name:
+        Name of the new generic level coordinate (e.g., `hybrid_height`).
+
+    Returns
+    -------
+    CoordinateInfo
+        New generic level coordinate.
+
+    """
+    new_coord = generic_level_coord.generic_lev_coords[new_coord_name]
+    new_coord.generic_level = True
+    new_coord.generic_lev_coords = (
+        var_info.coordinates[generic_level_coord_name].generic_lev_coords
+    )
+    return new_coord
+
+
+def _get_simplified_calendar(calendar: str) -> str:
+    """Simplify calendar."""
+    calendar_aliases = {
+        'all_leap': '366_day',
+        'noleap': '365_day',
+        'gregorian': 'standard',
+    }
+    return calendar_aliases.get(calendar, calendar)
+
+
+def _is_unstructured_grid(cube: Cube) -> bool:
+    """Check if cube uses unstructured grid."""
+    try:
+        lat = cube.coord('latitude')
+        lon = cube.coord('longitude')
+    except CoordinateNotFoundError:
+        pass
+    else:
+        if lat.ndim == 1 and (cube.coord_dims(lat) == cube.coord_dims(lon)):
+            return True
+    return False
+
+
+def _get_single_cube(
+    cube_list: Sequence[Cube],
+    short_name: str,
+    dataset_str: Optional[str] = None,
+) -> Cube:
+    if len(cube_list) == 1:
+        return cube_list[0]
+    cube = None
+    for raw_cube in cube_list:
+        if raw_cube.var_name == short_name:
+            cube = raw_cube
+            break
+
+    if dataset_str is None:
+        dataset_str = ''
+    else:
+        dataset_str = f' in {dataset_str}'
+
+    if not cube:
+        raise ValueError(
+            f"More than one cube found for variable {short_name}{dataset_str} "
+            f"but none of their var_names match the expected.\nFull list of "
+            f"cubes encountered: {cube_list}"
+        )
+    logger.warning(
+        "Found variable %s%s, but there were other present in the file. Those "
+        "extra variables are usually metadata (cell area, latitude "
+        "descriptions) that was not saved according to CF-conventions. It is "
+        "possible that errors appear further on because of this.\nFull list "
+        "of cubes encountered: %s", short_name, dataset_str, cube_list)
+    return cube
diff --git a/esmvalcore/cmor/check.py b/esmvalcore/cmor/check.py
index 4eb057730..43214168b 100644
--- a/esmvalcore/cmor/check.py
+++ b/esmvalcore/cmor/check.py
@@ -1,7 +1,12 @@
 """Module for checking iris cubes against their CMOR definitions."""
+from __future__ import annotations
+
 import logging
-from datetime import datetime
+import warnings
+from collections.abc import Callable
 from enum import IntEnum
+from functools import cached_property
+from typing import Optional
 
 import cf_units
 import iris.coord_categorisation
@@ -9,10 +14,18 @@ import iris.coords
 import iris.exceptions
 import iris.util
 import numpy as np
+from iris.cube import Cube
 
-from esmvalcore.iris_helpers import date2num
-
-from .table import CMOR_TABLES
+from esmvalcore.cmor._fixes.fix import GenericFix
+from esmvalcore.cmor._utils import (
+    _get_alternative_generic_lev_coord,
+    _get_generic_lev_coord_names,
+    _get_new_generic_level_coord,
+    _get_simplified_calendar,
+    _is_unstructured_grid,
+)
+from esmvalcore.cmor.table import get_var_info
+from esmvalcore.exceptions import ESMValCoreDeprecationWarning
 
 
 class CheckLevels(IntEnum):
@@ -34,49 +47,6 @@ class CheckLevels(IntEnum):
     """Do not fail for any discrepancy with CMOR standards."""
 
 
-def _get_next_month(month, year):
-    if month != 12:
-        return month + 1, year
-    return 1, year + 1
-
-
-def _get_time_bounds(time, freq):
-    bounds = []
-    dates = time.units.num2date(time.points)
-    for step, date in enumerate(dates):
-        month = date.month
-        year = date.year
-        if freq in ['mon', 'mo']:
-            next_month, next_year = _get_next_month(month, year)
-            min_bound = date2num(datetime(year, month, 1, 0, 0),
-                                 time.units, time.dtype)
-            max_bound = date2num(datetime(next_year, next_month, 1, 0, 0),
-                                 time.units, time.dtype)
-        elif freq == 'yr':
-            min_bound = date2num(datetime(year, 1, 1, 0, 0),
-                                 time.units, time.dtype)
-            max_bound = date2num(datetime(year + 1, 1, 1, 0, 0),
-                                 time.units, time.dtype)
-        elif freq == 'dec':
-            min_bound = date2num(datetime(year, 1, 1, 0, 0),
-                                 time.units, time.dtype)
-            max_bound = date2num(datetime(year + 10, 1, 1, 0, 0),
-                                 time.units, time.dtype)
-        else:
-            delta = {
-                'day': 12 / 24,
-                '6hr': 3 / 24,
-                '3hr': 1.5 / 24,
-                '1hr': 0.5 / 24,
-            }
-            point = time.points[step]
-            min_bound = point - delta[freq]
-            max_bound = point + delta[freq]
-        bounds.append([min_bound, max_bound])
-
-    return np.array(bounds)
-
-
 class CMORCheckError(Exception):
     """Exception raised when a cube does not pass the CMORCheck."""
 
@@ -84,9 +54,6 @@ class CMORCheckError(Exception):
 class CMORCheck():
     """Class used to check the CMOR-compliance of the data.
 
-    It can also fix some minor errors and does some minor data
-    homogeneization:
-
     Parameters
     ----------
     cube: iris.cube.Cube:
@@ -94,13 +61,23 @@ class CMORCheck():
     var_info: variables_info.VariableInfo
         Variable info to check.
     frequency: str
-        Expected frequency for the data.
+        Expected frequency for the data. If not given, use the one from the
+        variable information.
     fail_on_error: bool
         If true, CMORCheck stops on the first error. If false, it collects
         all possible errors before stopping.
     automatic_fixes: bool
         If True, CMORCheck will try to apply automatic fixes for any
         detected error, if possible.
+
+        .. deprecated:: 2.10.0
+            This option has been deprecated in ESMValCore version 2.10.0 and is
+            scheduled for removal in version 2.12.0. Please use the functions
+            :func:`~esmvalcore.preprocessor.fix_metadata`,
+            :func:`~esmvalcore.preprocessor.fix_data`, or
+            :meth:`esmvalcore.dataset.Dataset.load` (which automatically
+            includes the first two functions) instead. Fixes and CMOR checks
+            have been clearly separated in ESMValCore version 2.10.0.
     check_level: CheckLevels
         Level of strictness of the checks.
 
@@ -131,7 +108,6 @@ class CMORCheck():
         self._errors = list()
         self._warnings = list()
         self._debug_messages = list()
-        self._unstructured = None
 
         self._cmor_var = var_info
         if not frequency:
@@ -139,47 +115,61 @@ class CMORCheck():
         self.frequency = frequency
         self.automatic_fixes = automatic_fixes
 
-    def _is_unstructured_grid(self):
-        if self._unstructured is None:
-            self._unstructured = False
-            try:
-                lat = self._cube.coord('latitude')
-                lon = self._cube.coord('longitude')
-            except iris.exceptions.CoordinateNotFoundError:
-                pass
-            else:
-                if lat.ndim == 1 and (self._cube.coord_dims(lat)
-                                      == self._cube.coord_dims(lon)):
-                    self._unstructured = True
-        return self._unstructured
-
-    def check_metadata(self, logger=None):
+        # Deprecate automatic_fixes (remove in v2.12)
+        if automatic_fixes:
+            msg = (
+                "The option `automatic_fixes` has been deprecated in "
+                "ESMValCore version 2.10.0 and is scheduled for removal in "
+                "version 2.12.0. Please use the functions "
+                "esmvalcore.preprocessor.fix_metadata(), "
+                "esmvalcore.preprocessor.fix_data(), or "
+                "esmvalcore.dataset.Dataset.load() (which automatically "
+                "includes the first two functions) instead. Fixes and CMOR "
+                "checks have been clearly separated in ESMValCore version "
+                "2.10.0."
+            )
+            warnings.warn(msg, ESMValCoreDeprecationWarning)
+
+        # TODO: remove in v2.12
+
+        self._generic_fix = GenericFix(var_info, frequency=frequency)
+
+    @cached_property
+    def _unstructured_grid(self) -> bool:
+        """Cube uses unstructured grid."""
+        return _is_unstructured_grid(self._cube)
+
+    def check_metadata(self, logger: Optional[logging.Logger] = None) -> Cube:
         """Check the cube metadata.
 
-        Perform all the tests that do not require to have the data in memory.
-
-        It will also report some warnings in case of minor errors and
-        homogenize some data:
-
-            - Equivalent calendars will all default to the same name.
-            - Time units will be set to days since 1850-01-01
+        It will also report some warnings in case of minor errors.
 
 
         Parameters
         ----------
-        logger: logging.Logger
+        logger:
             Given logger.
 
+        Returns
+        -------
+        iris.cube.Cube
+            Checked cube.
+
         Raises
         ------
         CMORCheckError
             If errors are found. If fail_on_error attribute is set to True,
             raises as soon as an error is detected. If set to False, it perform
             all checks and then raises.
+
         """
         if logger is not None:
             self._logger = logger
 
+        # TODO: remove in v2.12
+        if self.automatic_fixes:
+            [self._cube] = self._generic_fix.fix_metadata([self._cube])
+
         self._check_var_metadata()
         self._check_fill_value()
         self._check_multiple_coords_same_stdname()
@@ -196,10 +186,9 @@ class CMORCheck():
 
         return self._cube
 
-    def check_data(self, logger=None):
+    def check_data(self, logger: Optional[logging.Logger] = None) -> Cube:
         """Check the cube data.
 
-        Performs all the tests that require to have the data in memory.
         Assumes that metadata is correct, so you must call check_metadata prior
         to this.
 
@@ -207,28 +196,35 @@ class CMORCheck():
 
         Parameters
         ----------
-        logger: logging.Logger
+        logger:
             Given logger.
 
+        Returns
+        -------
+        iris.cube.Cube
+            Checked cube.
+
         Raises
         ------
         CMORCheckError
             If errors are found. If fail_on_error attribute is set to True,
             raises as soon as an error is detected. If set to False, it perform
             all checks and then raises.
+
         """
         if logger is not None:
             self._logger = logger
 
-        if self._cmor_var.units:
-            units = self._get_effective_units()
-            if str(self._cube.units) != units:
-                self._cube.convert_units(units)
+        # TODO: remove in v2.12
+        if self.automatic_fixes:
+            self._cube = self._generic_fix.fix_data(self._cube)
 
         self._check_coords_data()
 
+        self.report_debug_messages()
         self.report_warnings()
         self.report_errors()
+
         return self._cube
 
     def report_errors(self):
@@ -251,13 +247,7 @@ class CMORCheck():
             raise CMORCheckError(msg)
 
     def report_warnings(self):
-        """Report detected warnings to the given logger.
-
-        Parameters
-        ----------
-        logger: logging.Logger
-            Given logger
-        """
+        """Report detected warnings to the given logger."""
         if self.has_warnings():
             msg = '\n'.join([
                 f'There were warnings in variable {self._cube.var_name}:',
@@ -268,13 +258,7 @@ class CMORCheck():
             self._logger.warning(msg)
 
     def report_debug_messages(self):
-        """Report detected debug messages to the given logger.
-
-        Parameters
-        ----------
-        logger: logging.Logger
-            Given logger.
-        """
+        """Report detected debug messages to the given logger."""
         if self.has_debug_messages():
             msg = '\n'.join([
                 f'There were metadata changes in variable '
@@ -298,49 +282,24 @@ class CMORCheck():
         # Check standard_name
         if self._cmor_var.standard_name:
             if self._cube.standard_name != self._cmor_var.standard_name:
-                if self.automatic_fixes:
-                    self.report_warning(
-                        'Standard name for {} changed from {} to {}',
-                        self._cube.var_name, self._cube.standard_name,
-                        self._cmor_var.standard_name)
-                    self._cube.standard_name = self._cmor_var.standard_name
-                else:
-                    self.report_error(self._attr_msg, self._cube.var_name,
-                                      'standard_name',
-                                      self._cmor_var.standard_name,
-                                      self._cube.standard_name)
+                self.report_error(self._attr_msg, self._cube.var_name,
+                                  'standard_name',
+                                  self._cmor_var.standard_name,
+                                  self._cube.standard_name)
         # Check long_name
         if self._cmor_var.long_name:
             if self._cube.long_name != self._cmor_var.long_name:
-                if self.automatic_fixes:
-                    self.report_warning(
-                        'Long name for {} changed from {} to {}',
-                        self._cube.var_name, self._cube.long_name,
-                        self._cmor_var.long_name)
-                    self._cube.long_name = self._cmor_var.long_name
-                else:
-                    self.report_error(self._attr_msg, self._cube.var_name,
-                                      'long_name', self._cmor_var.long_name,
-                                      self._cube.long_name)
+                self.report_error(self._attr_msg, self._cube.var_name,
+                                  'long_name', self._cmor_var.long_name,
+                                  self._cube.long_name)
 
         # Check units
-        if (self.automatic_fixes and self._cube.attributes.get(
-                'invalid_units', '').lower() == 'psu'):
-            self._cube.units = '1.0'
-            del self._cube.attributes['invalid_units']
-
         if self._cmor_var.units:
             units = self._get_effective_units()
             if self._cube.units != units:
-                if not self._cube.units.is_convertible(units):
-                    self.report_error(f'Variable {self._cube.var_name} units '
-                                      f'{self._cube.units} can not be '
-                                      f'converted to {self._cmor_var.units}')
-                else:
-                    self.report_warning(
-                        f'Variable {self._cube.var_name} units '
-                        f'{self._cube.units} will be '
-                        f'converted to {self._cmor_var.units}')
+                self.report_error(self._attr_msg, self._cube.var_name,
+                                  'units', self._cmor_var.units,
+                                  self._cube.units)
 
         # Check other variable attributes that match entries in cube.attributes
         attrs = ('positive', )
@@ -357,6 +316,7 @@ class CMORCheck():
 
     def _get_effective_units(self):
         """Get effective units."""
+        # TODO: remove entire function in v2.12
         if self._cmor_var.units.lower() == 'psu':
             units = '1.0'
         else:
@@ -423,20 +383,7 @@ class CMORCheck():
                 except iris.exceptions.CoordinateNotFoundError:
                     try:
                         coord = self._cube.coord(coordinate.standard_name)
-                        if self._cmor_var.table_type in 'CMIP6' and \
-                           coord.ndim > 1 and \
-                           coord.standard_name in ['latitude', 'longitude']:
-                            self.report_debug_message(
-                                'Multidimensional {0} coordinate is not set '
-                                'in CMOR standard. ESMValTool will change '
-                                'the original value of  {1} to {2} to match '
-                                'the one-dimensional case.',
-                                coordinate.standard_name,
-                                coord.var_name,
-                                coordinate.out_name,
-                            )
-                            coord.var_name = coordinate.out_name
-                        elif coord.standard_name in ['region', 'area_type']:
+                        if coord.standard_name in ['region', 'area_type']:
                             self.report_debug_message(
                                 'Coordinate {0} has var name {1} '
                                 'instead of {2}. '
@@ -467,33 +414,17 @@ class CMORCheck():
 
     def _check_generic_level_dim_names(self, key, coordinate):
         """Check name of generic level coordinate."""
-        standard_name = None
-        out_name = None
-        name = None
         if coordinate.generic_lev_coords:
-            for coord in coordinate.generic_lev_coords.values():
-                try:
-                    cube_coord = self._cube.coord(var_name=coord.out_name)
-                    out_name = coord.out_name
-                    if cube_coord.standard_name == coord.standard_name:
-                        standard_name = coord.standard_name
-                        name = coord.name
-                except iris.exceptions.CoordinateNotFoundError:
-                    try:
-                        cube_coord = self._cube.coord(
-                            var_name=coord.standard_name)
-                        standard_name = coord.standard_name
-                        name = coord.name
-                    except iris.exceptions.CoordinateNotFoundError:
-                        pass
+            (standard_name, out_name, name) = _get_generic_lev_coord_names(
+                self._cube, coordinate
+            )
             if standard_name:
                 if not out_name:
                     self.report_error(
                         f'Generic level coordinate {key} has wrong var_name.')
-                level = coordinate.generic_lev_coords[name]
-                level.generic_level = True
-                level.generic_lev_coords = self._cmor_var.coordinates[
-                    key].generic_lev_coords
+                level = _get_new_generic_level_coord(
+                    self._cmor_var, coordinate, key, name
+                )
                 self._cmor_var.coordinates[key] = level
                 self.report_debug_message(f'Generic level coordinate {key} '
                                           'will be checked against '
@@ -506,17 +437,6 @@ class CMORCheck():
                 else:
                     self._check_alternative_dim_names(key)
 
-    ALTERNATIVE_GENERIC_LEV_COORDS = {
-        'alevel': {
-            'CMIP5': ['alt40', 'plevs'],
-            'CMIP6': ['alt16', 'plev3'],
-            'obs4MIPs': ['alt16', 'plev3'],
-        },
-        'zlevel': {
-            'CMIP3': ['pressure'],
-        },
-    }
-
     def _check_alternative_dim_names(self, key):
         """Check for viable alternatives to generic level coordinates.
 
@@ -549,42 +469,34 @@ class CMORCheck():
         For ``cmor_strict=False`` project (like OBS) the check for requested
         values might be disabled.
         """
-        table_type = self._cmor_var.table_type
-        alternative_coord = None
-        allowed_alternatives = self.ALTERNATIVE_GENERIC_LEV_COORDS.get(
-            key, {}).get(table_type, [])
-
-        # Check if any of the allowed alternative coordinates is present in the
-        # cube
-        for allowed_alternative in allowed_alternatives:
-            coord_info = CMOR_TABLES[table_type].coords[allowed_alternative]
-            try:
-                cube_coord = self._cube.coord(var_name=coord_info.out_name)
-            except iris.exceptions.CoordinateNotFoundError:
-                pass
-            else:
-                if cube_coord.standard_name == coord_info.standard_name:
-                    alternative_coord = coord_info
-                    break
-                self.report_error(
-                    f"Found alternative coordinate '{coord_info.out_name}' "
-                    f"for generic level coordinate '{key}' with wrong "
-                    f"standard_name '{cube_coord.standard_name}' (expected "
-                    f"'{coord_info.standard_name}')")
-                break
+        try:
+            (alternative_coord,
+             cube_coord) = _get_alternative_generic_lev_coord(
+                self._cube, key, self._cmor_var.table_type
+            )
 
         # No valid alternative coordinate found -> critical error
-        if alternative_coord is None:
+        except ValueError:
             self.report_critical(self._does_msg, key, 'exist')
             return
 
+        # Wrong standard_name -> error
+        if cube_coord.standard_name != alternative_coord.standard_name:
+            self.report_error(
+                f"Found alternative coordinate '{alternative_coord.out_name}' "
+                f"for generic level coordinate '{key}' with wrong "
+                f"standard_name {cube_coord.standard_name}' (expected "
+                f"'{alternative_coord.standard_name}')"
+            )
+            return
+
         # Valid alternative coordinate found -> perform checks on it
         self.report_warning(
             f"Found alternative coordinate '{alternative_coord.out_name}' "
             f"for generic level coordinate '{key}'. Subsequent warnings about "
             f"levels that are not contained in '{alternative_coord.out_name}' "
             f"can be safely ignored.")
-        self._check_coord(alternative_coord, cube_coord, self._cube.var_name)
+        self._check_coord(alternative_coord, cube_coord, cube_coord.var_name)
 
     def _check_coords(self):
         """Check coordinates."""
@@ -616,6 +528,12 @@ class CMORCheck():
             except iris.exceptions.CoordinateNotFoundError:
                 continue
 
+            # TODO: remove in v2.12
+            if self.automatic_fixes:
+                (self._cube, coord) = self._generic_fix._fix_coord_direction(
+                    self._cube, coordinate, coord
+                )
+
             self._check_coord_monotonicity_and_direction(
                 coordinate, coord, var_name)
 
@@ -625,57 +543,24 @@ class CMORCheck():
             return
         if cmor.units:
             if str(coord.units) != cmor.units:
-                fixed = False
-                if self.automatic_fixes:
-                    try:
-                        old_unit = coord.units
-                        new_unit = cf_units.Unit(cmor.units,
-                                                 coord.units.calendar)
-                        coord.convert_units(new_unit)
-                        fixed = True
-                        self.report_warning(
-                            f'Coordinate {coord.var_name} units '
-                            f'{str(old_unit)} '
-                            f'converted to {cmor.units}')
-                    except ValueError:
-                        pass
-                if not fixed:
-                    self.report_critical(self._attr_msg, var_name, 'units',
-                                         cmor.units, coord.units)
+                self.report_critical(self._attr_msg, var_name, 'units',
+                                     cmor.units, coord.units)
         self._check_coord_points(cmor, coord, var_name)
 
     def _check_coord_bounds(self, cmor, coord, var_name):
         if cmor.must_have_bounds == 'yes' and not coord.has_bounds():
-            if self.automatic_fixes:
-                try:
-                    coord.guess_bounds()
-                except ValueError as ex:
-                    self.report_warning(
-                        'Can not guess bounds for coordinate {0} '
-                        'from var {1}: {2}', coord.var_name, var_name, ex)
-                else:
-                    self.report_warning(
-                        'Added guessed bounds to coordinate {0} from var {1}',
-                        coord.var_name, var_name)
-            else:
-                self.report_warning(
-                    'Coordinate {0} from var {1} does not have bounds',
-                    coord.var_name, var_name)
+            self.report_warning(
+                'Coordinate {0} from var {1} does not have bounds',
+                coord.var_name, var_name)
 
-    def _check_time_bounds(self, freq, time):
+    def _check_time_bounds(self, time):
         times = {'time', 'time1', 'time2', 'time3'}
         key = times.intersection(self._cmor_var.coordinates)
         cmor = self._cmor_var.coordinates[" ".join(key)]
         if cmor.must_have_bounds == 'yes' and not time.has_bounds():
-            if self.automatic_fixes:
-                time.bounds = _get_time_bounds(time, freq)
-                self.report_warning(
-                    'Added guessed bounds to coordinate {0} from var {1}',
-                    time.var_name, self._cmor_var.short_name)
-            else:
-                self.report_warning(
-                    'Coordinate {0} from var {1} does not have bounds',
-                    time.var_name, self._cmor_var.short_name)
+            self.report_warning(
+                'Coordinate {0} from var {1} does not have bounds',
+                time.var_name, self._cmor_var.short_name)
 
     def _check_coord_monotonicity_and_direction(self, cmor, coord, var_name):
         """Check monotonicity and direction of coordinate."""
@@ -684,8 +569,8 @@ class CMORCheck():
         if coord.dtype.kind == 'U':
             return
 
-        if self._is_unstructured_grid() and \
-           coord.standard_name in ['latitude', 'longitude']:
+        if (self._unstructured_grid and
+                coord.standard_name in ['latitude', 'longitude']):
             self.report_debug_message(
                 f'Coordinate {coord.standard_name} appears to belong to '
                 'an unstructured grid. Skipping monotonicity and '
@@ -694,118 +579,42 @@ class CMORCheck():
 
         if not coord.is_monotonic():
             self.report_critical(self._is_msg, var_name, 'monotonic')
+
         if len(coord.core_points()) == 1:
             return
+
         if cmor.stored_direction:
             if cmor.stored_direction == 'increasing':
                 if coord.core_points()[0] > coord.core_points()[1]:
-                    if not self.automatic_fixes or coord.ndim > 1:
-                        self.report_critical(self._is_msg, var_name,
-                                             'increasing')
-                    else:
-                        self._reverse_coord(coord)
+                    self.report_critical(self._is_msg, var_name, 'increasing')
             elif cmor.stored_direction == 'decreasing':
                 if coord.core_points()[0] < coord.core_points()[1]:
-                    if not self.automatic_fixes or coord.ndim > 1:
-                        self.report_critical(self._is_msg, var_name,
-                                             'decreasing')
-                    else:
-                        self._reverse_coord(coord)
-
-    def _reverse_coord(self, coord):
-        """Reverse coordinate."""
-        if coord.ndim == 1:
-            self._cube = iris.util.reverse(self._cube,
-                                           self._cube.coord_dims(coord))
-            reversed_coord = self._cube.coord(var_name=coord.var_name)
-            if reversed_coord.has_bounds():
-                bounds = reversed_coord.bounds
-                right_bounds = bounds[:-2, 1]
-                left_bounds = bounds[1:-1, 0]
-                if np.all(right_bounds != left_bounds):
-                    reversed_coord.bounds = np.fliplr(bounds)
-                    coord = reversed_coord
-            self.report_debug_message(f'Coordinate {coord.var_name} values'
-                                      'have been reversed.')
+                    self.report_critical(self._is_msg, var_name, 'decreasing')
 
     def _check_coord_points(self, coord_info, coord, var_name):
         """Check coordinate points: values, bounds and monotonicity."""
         # Check requested coordinate values exist in coord.points
         self._check_requested_values(coord, coord_info, var_name)
 
-        l_fix_coord_value = False
-
         # Check coordinate value ranges
         if coord_info.valid_min:
             valid_min = float(coord_info.valid_min)
             if np.any(coord.core_points() < valid_min):
-                if coord_info.standard_name == 'longitude' and \
-                        self.automatic_fixes:
-                    l_fix_coord_value = self._check_longitude_min(
-                        coord, var_name)
-                else:
-                    self.report_critical(self._vals_msg, var_name,
-                                         '< {} ='.format('valid_min'),
-                                         valid_min)
+                self.report_critical(self._vals_msg, var_name,
+                                     '< {} ='.format('valid_min'),
+                                     valid_min)
 
         if coord_info.valid_max:
             valid_max = float(coord_info.valid_max)
             if np.any(coord.core_points() > valid_max):
-                if coord_info.standard_name == 'longitude' and \
-                        self.automatic_fixes:
-                    l_fix_coord_value = self._check_longitude_max(
-                        coord, var_name)
-                else:
-                    self.report_critical(self._vals_msg, var_name,
-                                         '> {} ='.format('valid_max'),
-                                         valid_max)
-
-        if l_fix_coord_value:
-            # cube.intersection only works for cells with 0 or 2 bounds
-            # Note: nbounds==0 means there are no bounds given, nbounds==2
-            # implies a regular grid with bounds in the grid direction,
-            # nbounds>2 implies an irregular grid with bounds given as vertices
-            # of the cell polygon.
-            if coord.ndim == 1 and coord.nbounds in (0, 2):
-                lon_extent = iris.coords.CoordExtent(coord, 0.0, 360., True,
-                                                     False)
-                self._cube = self._cube.intersection(lon_extent)
-            else:
-                new_lons = coord.core_points().copy()
-                new_lons = self._set_range_in_0_360(new_lons)
-                if coord.core_bounds() is not None:
-                    new_bounds = coord.core_bounds().copy()
-                    new_bounds = self._set_range_in_0_360(new_bounds)
-                else:
-                    new_bounds = None
-                new_coord = coord.copy(new_lons, new_bounds)
-                dims = self._cube.coord_dims(coord)
-                self._cube.remove_coord(coord)
-                self._cube.add_aux_coord(new_coord, dims)
-            coord = self._cube.coord(var_name=var_name)
+                self.report_critical(self._vals_msg, var_name,
+                                     '> {} ='.format('valid_max'),
+                                     valid_max)
+
         self._check_coord_bounds(coord_info, coord, var_name)
         self._check_coord_monotonicity_and_direction(coord_info, coord,
                                                      var_name)
 
-    def _check_longitude_max(self, coord, var_name):
-        if np.any(coord.core_points() > 720):
-            self.report_critical(
-                f'{var_name} longitude coordinate has values > 720 degrees')
-            return False
-        return True
-
-    def _check_longitude_min(self, coord, var_name):
-        if np.any(coord.core_points() < -360):
-            self.report_critical(
-                f'{var_name} longitude coordinate has values < -360 degrees')
-            return False
-        return True
-
-    @staticmethod
-    def _set_range_in_0_360(array):
-        """Convert longitude coordinate to [0, 360]."""
-        return (array + 360.0) % 360.0
-
     def _check_requested_values(self, coord, coord_info, var_name):
         """Check requested values."""
         if coord_info.requested:
@@ -818,17 +627,6 @@ class CMORCheck():
                 cmor_points = np.array(coord_info.requested, dtype=float)
             except ValueError:
                 cmor_points = coord_info.requested
-            else:
-                atol = 1e-7 * np.mean(cmor_points)
-                if (self.automatic_fixes
-                        and coord.core_points().shape == cmor_points.shape
-                        and np.allclose(
-                            coord.core_points(),
-                            cmor_points,
-                            rtol=1e-7,
-                            atol=atol,
-                        )):
-                    coord.points = cmor_points
             for point in cmor_points:
                 if point not in coord.core_points():
                     self.report_warning(self._contain_msg, var_name,
@@ -853,12 +651,7 @@ class CMORCheck():
             self.report_critical(self._does_msg, var_name,
                                  'have time reference units')
         else:
-            old_units = coord.units
-            coord.convert_units(
-                cf_units.Unit('days since 1850-1-1 00:00:00',
-                              calendar=coord.units.calendar))
-            simplified_cal = self._simplify_calendar(coord.units.calendar)
-            coord.units = cf_units.Unit(coord.units.origin, simplified_cal)
+            simplified_cal = _get_simplified_calendar(coord.units.calendar)
             attrs = self._cube.attributes
             parent_time = 'parent_time_units'
             if parent_time in attrs:
@@ -866,8 +659,7 @@ class CMORCheck():
                     pass
                 else:
                     try:
-                        parent_units = cf_units.Unit(attrs[parent_time],
-                                                     simplified_cal)
+                        cf_units.Unit(attrs[parent_time], simplified_cal)
                     except ValueError:
                         self.report_warning('Attribute parent_time_units has '
                                             'a wrong format and cannot be '
@@ -875,17 +667,8 @@ class CMORCheck():
                                             'be added to convert properly '
                                             'attributes branch_time_in_parent '
                                             'and branch_time_in_child.')
-                    else:
-                        attrs[parent_time] = 'days since 1850-1-1 00:00:00'
-                        branch_parent = 'branch_time_in_parent'
-                        if branch_parent in attrs:
-                            attrs[branch_parent] = parent_units.convert(
-                                attrs[branch_parent], coord.units)
-                        branch_child = 'branch_time_in_child'
-                        if branch_child in attrs:
-                            attrs[branch_child] = old_units.convert(
-                                attrs[branch_child], coord.units)
 
+        # Check frequency
         tol = 0.001
         intervals = {'dec': (3600, 3660), 'day': (1, 1)}
         freq = self.frequency
@@ -941,18 +724,8 @@ class CMORCheck():
                     msg = '{}: Frequency {} does not match input data'
                     self.report_error(msg, var_name, freq)
                     break
-        self._check_time_bounds(freq, coord)
-        # remove time_origin from attributes
-        coord.attributes.pop('time_origin', None)
-
-    @staticmethod
-    def _simplify_calendar(calendar):
-        calendar_aliases = {
-            'all_leap': '366_day',
-            'noleap': '365_day',
-            'gregorian': 'standard',
-        }
-        return calendar_aliases.get(calendar, calendar)
+
+        self._check_time_bounds(coord)
 
     def has_errors(self):
         """Check if there are reported errors.
@@ -1071,30 +844,19 @@ class CMORCheck():
         self.report(CheckLevels.DEBUG, message, *args)
 
 
-def _get_cmor_checker(table,
-                      mip,
-                      short_name,
-                      frequency,
-                      fail_on_error=False,
-                      check_level=CheckLevels.DEFAULT,
-                      automatic_fixes=False):
-    """Get a CMOR checker/fixer."""
-    if table not in CMOR_TABLES:
-        raise NotImplementedError(
-            "No CMOR checker implemented for table {}."
-            "\nThe following options are available: {}".format(
-                table, ', '.join(CMOR_TABLES)))
-
-    cmor_table = CMOR_TABLES[table]
-    if table == 'CORDEX' and mip.endswith('hr'):
-        # CORDEX X-hourly tables define the mip
-        # as ending in 'h' instead of 'hr'.
-        mip = mip.replace('hr', 'h')
-    var_info = cmor_table.get_variable(mip, short_name)
-    if var_info is None:
-        var_info = CMOR_TABLES['custom'].get_variable(mip, short_name)
-
-    def _checker(cube):
+def _get_cmor_checker(
+    project: str,
+    mip: str,
+    short_name: str,
+    frequency: None | str = None,
+    fail_on_error: bool = False,
+    check_level: CheckLevels = CheckLevels.DEFAULT,
+    automatic_fixes: bool = False,  # TODO: remove in v2.12
+) -> Callable[[Cube], CMORCheck]:
+    """Get a CMOR checker."""
+    var_info = get_var_info(project, mip, short_name)
+
+    def _checker(cube: Cube) -> CMORCheck:
         return CMORCheck(cube,
                          var_info,
                          frequency=frequency,
@@ -1105,105 +867,143 @@ def _get_cmor_checker(table,
     return _checker
 
 
-def cmor_check_metadata(cube,
-                        cmor_table,
-                        mip,
-                        short_name,
-                        frequency,
-                        check_level=CheckLevels.DEFAULT):
+def cmor_check_metadata(
+    cube: Cube,
+    cmor_table: str,
+    mip: str,
+    short_name: str,
+    frequency: Optional[str] = None,
+    check_level: CheckLevels = CheckLevels.DEFAULT,
+) -> Cube:
     """Check if metadata conforms to variable's CMOR definition.
 
     None of the checks at this step will force the cube to load the data.
 
     Parameters
     ----------
-    cube: iris.cube.Cube
+    cube:
         Data cube to check.
-    cmor_table: str
-        CMOR definitions to use.
+    cmor_table:
+        CMOR definitions to use (i.e., the variable's project).
     mip:
-        Variable's mip.
-    short_name: str
+        Variable's MIP.
+    short_name:
         Variable's short name.
-    frequency: str
-        Data frequency.
-    check_level: CheckLevels
+    frequency:
+        Data frequency. If not given, use the one from the CMOR table of the
+        variable.
+    check_level:
         Level of strictness of the checks.
+
+    Returns
+    -------
+    iris.cube.Cube
+        Checked cube.
+
     """
-    checker = _get_cmor_checker(cmor_table,
-                                mip,
-                                short_name,
-                                frequency,
-                                check_level=check_level)
-    checker(cube).check_metadata()
+    checker = _get_cmor_checker(
+        cmor_table,
+        mip,
+        short_name,
+        frequency=frequency,
+        check_level=check_level,
+    )
+    cube = checker(cube).check_metadata()
     return cube
 
 
-def cmor_check_data(cube,
-                    cmor_table,
-                    mip,
-                    short_name,
-                    frequency,
-                    check_level=CheckLevels.DEFAULT):
+def cmor_check_data(
+    cube: Cube,
+    cmor_table: str,
+    mip: str,
+    short_name: str,
+    frequency: Optional[str] = None,
+    check_level: CheckLevels = CheckLevels.DEFAULT,
+) -> Cube:
     """Check if data conforms to variable's CMOR definition.
 
-    The checks performed at this step require the data in memory.
-
     Parameters
     ----------
-    cube: iris.cube.Cube
+    cube:
         Data cube to check.
-    cmor_table: str
-        CMOR definitions to use.
+    cmor_table:
+        CMOR definitions to use (i.e., the variable's project).
     mip:
-        Variable's mip.
-    short_name: str
+        Variable's MIP.
+    short_name:
         Variable's short name
-    frequency: str
-        Data frequency
-    check_level: CheckLevels
+    frequency:
+        Data frequency. If not given, use the one from the CMOR table of the
+        variable.
+    check_level:
         Level of strictness of the checks.
+
+    Returns
+    -------
+    iris.cube.Cube
+        Checked cube.
+
     """
-    checker = _get_cmor_checker(cmor_table,
-                                mip,
-                                short_name,
-                                frequency,
-                                check_level=check_level)
-    checker(cube).check_data()
+    checker = _get_cmor_checker(
+        cmor_table,
+        mip,
+        short_name,
+        frequency=frequency,
+        check_level=check_level,
+    )
+    cube = checker(cube).check_data()
     return cube
 
 
-def cmor_check(cube, cmor_table, mip, short_name, frequency, check_level):
+def cmor_check(
+    cube: Cube,
+    cmor_table: str,
+    mip: str,
+    short_name: str,
+    frequency: Optional[str] = None,
+    check_level: CheckLevels = CheckLevels.DEFAULT,
+) -> Cube:
     """Check if cube conforms to variable's CMOR definition.
 
-    Equivalent to calling cmor_check_metadata and cmor_check_data
-    consecutively.
+    Equivalent to calling :func:`cmor_check_metadata` and
+    :func:`cmor_check_data` consecutively.
 
     Parameters
     ----------
-    cube: iris.cube.Cube
+    cube:
         Data cube to check.
-    cmor_table: str
-        CMOR definitions to use.
+    cmor_table:
+        CMOR definitions to use (i.e., the variable's project).
     mip:
-        Variable's mip.
-    short_name: str
+        Variable's MIP.
+    short_name:
         Variable's short name.
-    frequency: str
-        Data frequency.
-    check_level: enum.IntEnum
+    frequency:
+        Data frequency. If not given, use the one from the CMOR table of the
+        variable.
+    check_level:
         Level of strictness of the checks.
+
+    Returns
+    -------
+    iris.cube.Cube
+        Checked cube.
+
     """
-    cmor_check_metadata(cube,
-                        cmor_table,
-                        mip,
-                        short_name,
-                        frequency,
-                        check_level=check_level)
-    cmor_check_data(cube,
-                    cmor_table,
-                    mip,
-                    short_name,
-                    frequency,
-                    check_level=check_level)
+    cube = cmor_check_metadata(
+        cube,
+        cmor_table,
+        mip,
+        short_name,
+        frequency=frequency,
+        check_level=check_level,
+    )
+    cube = cmor_check_data(
+        cube,
+        cmor_table,
+        mip,
+        short_name,
+        frequency=frequency,
+        check_level=check_level,
+    )
     return cube
diff --git a/esmvalcore/cmor/fix.py b/esmvalcore/cmor/fix.py
index 1034435f9..d05af5ef6 100644
--- a/esmvalcore/cmor/fix.py
+++ b/esmvalcore/cmor/fix.py
@@ -7,14 +7,17 @@ variables to be sure that all known errors are fixed.
 from __future__ import annotations
 
 import logging
+import warnings
 from collections import defaultdict
+from collections.abc import Sequence
 from pathlib import Path
 from typing import TYPE_CHECKING, Optional
 
-from iris.cube import CubeList
+from iris.cube import Cube, CubeList
 
-from ._fixes.fix import Fix
-from .check import CheckLevels, _get_cmor_checker
+from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor.check import CheckLevels, _get_cmor_checker
+from esmvalcore.exceptions import ESMValCoreDeprecationWarning
 
 if TYPE_CHECKING:
     from ..config import Session
@@ -31,11 +34,12 @@ def fix_file(
     output_dir: Path,
     add_unique_suffix: bool = False,
     session: Optional[Session] = None,
+    frequency: Optional[str] = None,
     **extra_facets,
 ) -> str | Path:
     """Fix files before ESMValTool can load them.
 
-    This fixes are only for issues that prevent iris from loading the cube or
+    These fixes are only for issues that prevent iris from loading the cube or
     that cannot be fixed after the cube is loaded.
 
     Original files are not overwritten.
@@ -58,6 +62,8 @@ def fix_file(
         Adds a unique suffix to `output_dir` for thread safety.
     session:
         Current session which includes configuration and directory information.
+    frequency:
+        Variable's data frequency, if available.
     **extra_facets:
         Extra facets are mainly used for data outside of the big projects like
         CMIP, CORDEX, obs4MIPs. For details, see :ref:`extra_facets`.
@@ -75,6 +81,7 @@ def fix_file(
         'project': project,
         'dataset': dataset,
         'mip': mip,
+        'frequency': frequency,
     })
 
     for fix in Fix.get_fixes(project=project,
@@ -82,61 +89,81 @@ def fix_file(
                              mip=mip,
                              short_name=short_name,
                              extra_facets=extra_facets,
-                             session=session):
+                             session=session,
+                             frequency=frequency):
         file = fix.fix_file(
             file, output_dir, add_unique_suffix=add_unique_suffix
         )
     return file
 
 
-def fix_metadata(cubes,
-                 short_name,
-                 project,
-                 dataset,
-                 mip,
-                 frequency=None,
-                 check_level=CheckLevels.DEFAULT,
-                 session: Optional[Session] = None,
-                 **extra_facets):
-    """Fix cube metadata if fixes are required and check it anyway.
+def fix_metadata(
+    cubes: Sequence[Cube],
+    short_name: str,
+    project: str,
+    dataset: str,
+    mip: str,
+    frequency: Optional[str] = None,
+    check_level: CheckLevels = CheckLevels.DEFAULT,
+    session: Optional[Session] = None,
+    **extra_facets,
+) -> CubeList:
+    """Fix cube metadata if fixes are required.
 
-    This method collects all the relevant fixes for a given variable, applies
-    them and checks the resulting cube (or the original if no fixes were
-    needed) metadata to ensure that it complies with the standards of its
-    project CMOR tables.
+    This method collects all the relevant fixes (including generic ones) for a
+    given variable and applies them.
 
     Parameters
     ----------
-    cubes: iris.cube.CubeList
+    cubes:
         Cubes to fix.
-    short_name: str
+    short_name:
         Variable's short name.
-    project: str
+    project:
         Project of the dataset.
-    dataset: str
+    dataset:
         Name of the dataset.
-    mip: str
+    mip:
         Variable's MIP.
-    frequency: str, optional
+    frequency:
         Variable's data frequency, if available.
-    check_level: CheckLevels
-        Level of strictness of the checks. Set to default.
-    session: Session, optional
+    check_level:
+        Level of strictness of the checks.
+
+        .. deprecated:: 2.10.0
+            This option has been deprecated in ESMValCore version 2.10.0 and is
+            scheduled for removal in version 2.12.0. Please use the functions
+            :func:`~esmvalcore.preprocessor.cmor_check_metadata`,
+            :func:`~esmvalcore.preprocessor.cmor_check_data`, or
+            :meth:`~esmvalcore.cmor.check.cmor_check` instead. This function
+            will no longer perform CMOR checks. Fixes and CMOR checks have been
+            clearly separated in ESMValCore version 2.10.0.
+    session:
         Current session which includes configuration and directory information.
-    **extra_facets: dict, optional
+    **extra_facets:
         Extra facets are mainly used for data outside of the big projects like
         CMIP, CORDEX, obs4MIPs. For details, see :ref:`extra_facets`.
 
     Returns
     -------
-    iris.cube.Cube:
-        Fixed and checked cube.
+    iris.cube.CubeList
+        Fixed cubes.
 
-    Raises
-    ------
-    CMORCheckError
-        If the checker detects errors in the metadata that it can not fix.
     """
+    # Deprecate CMOR checks (remove in v2.12)
+    if check_level != CheckLevels.DEFAULT:
+        msg = (
+            "The option `check_level` has been deprecated in ESMValCore "
+            "version 2.10.0 and is scheduled for removal in version 2.12.0. "
+            "Please use the functions "
+            "esmvalcore.preprocessor.cmor_check_metadata, "
+            "esmvalcore.preprocessor.cmor_check_data, or "
+            "esmvalcore.cmor.check.cmor_check instead. This function will no "
+            "longer perform CMOR checks. Fixes and CMOR checks have been "
+            "clearly separated in ESMValCore version 2.10.0."
+        )
+        warnings.warn(msg, ESMValCoreDeprecationWarning)
+
     # Update extra_facets with variable information given as regular arguments
     # to this function
     extra_facets.update({
@@ -152,8 +179,12 @@ def fix_metadata(cubes,
                           mip=mip,
                           short_name=short_name,
                           extra_facets=extra_facets,
-                          session=session)
-    fixed_cubes = []
+                          session=session,
+                          frequency=frequency)
+    fixed_cubes = CubeList()
+
+    # Group cubes by input file and apply all fixes to each group element
+    # (i.e., each file) individually
     by_file = defaultdict(list)
     for cube in cubes:
         by_file[cube.attributes.get('source_file', '')].append(cube)
@@ -163,94 +194,97 @@ def fix_metadata(cubes,
         for fix in fixes:
             cube_list = fix.fix_metadata(cube_list)
 
-        cube = _get_single_cube(cube_list, short_name, project, dataset)
-        checker = _get_cmor_checker(frequency=frequency,
-                                    table=project,
-                                    mip=mip,
-                                    short_name=short_name,
-                                    check_level=check_level,
-                                    fail_on_error=False,
-                                    automatic_fixes=True)
+        # The final fix is always GenericFix, whose fix_metadata method always
+        # returns a single cube
+        cube = cube_list[0]
+
+        # Perform CMOR checks
+        # TODO: remove in v2.12
+        checker = _get_cmor_checker(
+            project,
+            mip,
+            short_name,
+            frequency,
+            fail_on_error=False,
+            check_level=check_level,
+        )
         cube = checker(cube).check_metadata()
+
         cube.attributes.pop('source_file', None)
         fixed_cubes.append(cube)
-    return fixed_cubes
 
-
-def _get_single_cube(cube_list, short_name, project, dataset):
-    if len(cube_list) == 1:
-        return cube_list[0]
-    cube = None
-    for raw_cube in cube_list:
-        if raw_cube.var_name == short_name:
-            cube = raw_cube
-            break
-    if not cube:
-        raise ValueError(
-            f'More than one cube found for variable {short_name} in '
-            f'{project}:{dataset} but none of their var_names match the '
-            f'expected.\nFull list of cubes encountered: {cube_list}'
-        )
-    logger.warning(
-        'Found variable %s in %s:%s, but there were other present in '
-        'the file. Those extra variables are usually metadata '
-        '(cell area, latitude descriptions) that was not saved '
-        'according to CF-conventions. It is possible that errors appear '
-        'further on because of this. \nFull list of cubes encountered: %s',
-        short_name, project, dataset, cube_list)
-    return cube
+    return fixed_cubes
 
 
-def fix_data(cube,
-             short_name,
-             project,
-             dataset,
-             mip,
-             frequency=None,
-             check_level=CheckLevels.DEFAULT,
-             session: Optional[Session] = None,
-             **extra_facets):
-    """Fix cube data if fixes add present and check it anyway.
+def fix_data(
+    cube: Cube,
+    short_name: str,
+    project: str,
+    dataset: str,
+    mip: str,
+    frequency: Optional[str] = None,
+    check_level: CheckLevels = CheckLevels.DEFAULT,
+    session: Optional[Session] = None,
+    **extra_facets,
+) -> Cube:
+    """Fix cube data if fixes are required.
 
     This method assumes that metadata is already fixed and checked.
 
-    This method collects all the relevant fixes for a given variable, applies
-    them and checks resulting cube (or the original if no fixes were
-    needed) metadata to ensure that it complies with the standards of its
-    project CMOR tables.
+    This method collects all the relevant fixes (including generic ones) for a
+    given variable and applies them.
 
     Parameters
     ----------
-    cube: iris.cube.Cube
+    cube:
         Cube to fix.
-    short_name: str
+    short_name:
         Variable's short name.
-    project: str
+    project:
         Project of the dataset.
-    dataset: str
+    dataset:
         Name of the dataset.
-    mip: str
+    mip:
         Variable's MIP.
-    frequency: str, optional
+    frequency:
         Variable's data frequency, if available.
-    check_level: CheckLevels
-        Level of strictness of the checks. Set to default.
-    session: Session, optional
+    check_level:
+        Level of strictness of the checks.
+
+        .. deprecated:: 2.10.0
+            This option has been deprecated in ESMValCore version 2.10.0 and is
+            scheduled for removal in version 2.12.0. Please use the functions
+            :func:`~esmvalcore.preprocessor.cmor_check_metadata`,
+            :func:`~esmvalcore.preprocessor.cmor_check_data`, or
+            :meth:`~esmvalcore.cmor.check.cmor_check` instead. This function
+            will no longer perform CMOR checks. Fixes and CMOR checks have been
+            clearly separated in ESMValCore version 2.10.0.
+    session:
         Current session which includes configuration and directory information.
-    **extra_facets: dict, optional
+    **extra_facets:
         Extra facets are mainly used for data outside of the big projects like
         CMIP, CORDEX, obs4MIPs. For details, see :ref:`extra_facets`.
 
     Returns
     -------
-    iris.cube.Cube:
-        Fixed and checked cube.
+    iris.cube.Cube
+        Fixed cube.
 
-    Raises
-    ------
-    CMORCheckError
-        If the checker detects errors in the data that it can not fix.
     """
+    # Deprecate CMOR checks (remove in v2.12)
+    if check_level != CheckLevels.DEFAULT:
+        msg = (
+            "The option `check_level` has been deprecated in ESMValCore "
+            "version 2.10.0 and is scheduled for removal in version 2.12.0. "
+            "Please use the functions "
+            "esmvalcore.preprocessor.cmor_check_metadata, "
+            "esmvalcore.preprocessor.cmor_check_data, or "
+            "esmvalcore.cmor.check.cmor_check instead. This function will no "
+            "longer perform CMOR checks. Fixes and CMOR checks have been "
+            "clearly separated in ESMValCore version 2.10.0."
+        )
+        warnings.warn(msg, ESMValCoreDeprecationWarning)
+
     # Update extra_facets with variable information given as regular arguments
     # to this function
     extra_facets.update({
@@ -266,14 +300,20 @@ def fix_data(cube,
                              mip=mip,
                              short_name=short_name,
                              extra_facets=extra_facets,
-                             session=session):
+                             session=session,
+                             frequency=frequency):
         cube = fix.fix_data(cube)
-    checker = _get_cmor_checker(frequency=frequency,
-                                table=project,
-                                mip=mip,
-                                short_name=short_name,
-                                fail_on_error=False,
-                                automatic_fixes=True,
-                                check_level=check_level)
+
+    # Perform CMOR checks
+    # TODO: remove in v2.12
+    checker = _get_cmor_checker(
+        project,
+        mip,
+        short_name,
+        frequency,
+        fail_on_error=False,
+        check_level=check_level,
+    )
     cube = checker(cube).check_data()
+
     return cube
diff --git a/esmvalcore/cmor/table.py b/esmvalcore/cmor/table.py
index 504a31c13..01659aa20 100644
--- a/esmvalcore/cmor/table.py
+++ b/esmvalcore/cmor/table.py
@@ -70,18 +70,50 @@ def _get_mips(project: str, short_name: str) -> list[str]:
     return mips
 
 
-def get_var_info(project, mip, short_name):
+def get_var_info(
+    project: str,
+    mip: str,
+    short_name: str,
+) -> VariableInfo | None:
     """Get variable information.
 
+    Note
+    ----
+    If `project=CORDEX` and the `mip` ends with 'hr', it is cropped to 'h'
+    since CORDEX X-hourly tables define the `mip` as ending in 'h' instead of
+    'hr'.
+
     Parameters
     ----------
-    project : str
+    project:
         Dataset's project.
-    mip : str
-        Variable's cmor table.
-    short_name : str
+    mip:
+        Variable's CMOR table, i.e., MIP.
+    short_name:
         Variable's short name.
+
+    Returns
+    -------
+    VariableInfo | None
+        `VariableInfo` object for the requested variable if found, ``None``
+        otherwise.
+
+    Raises
+    ------
+    KeyError
+        No CMOR tables available for `project`.
+
     """
+    if project not in CMOR_TABLES:
+        raise KeyError(
+            f"No CMOR tables available for project '{project}'. The following "
+            f"tables are available: {', '.join(CMOR_TABLES)}."
+        )
+
+    # CORDEX X-hourly tables define the mip as ending in 'h' instead of 'hr'
+    if project == 'CORDEX' and mip.endswith('hr'):
+        mip = mip.replace('hr', 'h')
+
     return CMOR_TABLES[project].get_variable(mip, short_name)
 
 
@@ -193,6 +225,7 @@ class InfoBase():
         If False, will look for a variable in other tables if it can not be
         found in the requested one
     """
+
     def __init__(self, default, alt_names, strict):
         if alt_names is None:
             alt_names = ""
@@ -217,28 +250,35 @@ class InfoBase():
         """
         return self.tables.get(table)
 
-    def get_variable(self, table_name, short_name, derived=False):
-        """Search and return the variable info.
+    def get_variable(
+        self,
+        table_name: str,
+        short_name: str,
+        derived: Optional[bool] = False,
+    ) -> VariableInfo | None:
+        """Search and return the variable information.
 
         Parameters
         ----------
-        table_name: str
-            Table name
-        short_name: str
-            Variable's short name
-        derived: bool, optional
-            Variable is derived. Info retrieval for derived variables always
-            look on the default tables if variable is not find in the
-            requested table
+        table_name:
+            Table name, i.e., the variable's MIP.
+        short_name:
+            Variable's short name.
+        derived:
+            Variable is derived. Information retrieval for derived variables
+            always looks in the default tables (usually, the custom tables) if
+            variable is not found in the requested table.
 
         Returns
         -------
-        VariableInfo
-            Return the VariableInfo object for the requested variable if
-            found, returns None if not
+        VariableInfo | None
+            `VariableInfo` object for the requested variable if found, ``None``
+            otherwise.
+
         """
         alt_names_list = self._get_alt_names_list(short_name)
 
+        # First, look in requested table
         table = self.get_table(table_name)
         if table:
             for alt_names in alt_names_list:
@@ -247,10 +287,19 @@ class InfoBase():
                 except KeyError:
                     pass
 
+        # If that didn't work, look in all tables (i.e., other MIPs) if
+        # cmor_strict=False
         var_info = self._look_in_all_tables(alt_names_list)
+
+        # If that didn' work either, look in default table if cmor_strict=False
+        # or derived=True
         if not var_info:
             var_info = self._look_in_default(derived, alt_names_list,
                                              table_name)
+
+        # If necessary, adapt frequency of variable (set it to the one from the
+        # requested MIP). E.g., if the user asked for table `Amon`, but the
+        # variable has been found in `day`, use frequency `mon`.
         if var_info:
             var_info = var_info.copy()
             var_info = self._update_frequency_from_mip(table_name, var_info)
@@ -258,6 +307,7 @@ class InfoBase():
         return var_info
 
     def _look_in_default(self, derived, alt_names_list, table_name):
+        """Look for variable in default table."""
         var_info = None
         if (not self.strict or derived):
             for alt_names in alt_names_list:
@@ -267,6 +317,7 @@ class InfoBase():
         return var_info
 
     def _look_in_all_tables(self, alt_names_list):
+        """Look for variable in all tables."""
         var_info = None
         if not self.strict:
             for alt_names in alt_names_list:
@@ -276,6 +327,7 @@ class InfoBase():
         return var_info
 
     def _get_alt_names_list(self, short_name):
+        """Get list of alternative variable names."""
         alt_names_list = [short_name]
         for alt_names in self.alt_names:
             if short_name in alt_names:
@@ -286,12 +338,14 @@ class InfoBase():
         return alt_names_list
 
     def _update_frequency_from_mip(self, table_name, var_info):
+        """Update frequency information of var_info from table."""
         mip_info = self.get_table(table_name)
         if mip_info:
             var_info.frequency = mip_info.frequency
         return var_info
 
     def _look_all_tables(self, alt_names):
+        """Look for variable in all tables."""
         for table_vars in sorted(self.tables.values()):
             if alt_names in table_vars:
                 return table_vars[alt_names]
@@ -315,6 +369,7 @@ class CMIP6Info(InfoBase):
         If False, will look for a variable in other tables if it can not be
         found in the requested one
     """
+
     def __init__(self,
                  cmor_tables_path,
                  default=None,
@@ -471,6 +526,7 @@ class CMIP6Info(InfoBase):
 @total_ordering
 class TableInfo(dict):
     """Container class for storing a CMOR table."""
+
     def __init__(self, *args, **kwargs):
         """Create a new TableInfo object for storing VariableInfo objects."""
         super(TableInfo, self).__init__(*args, **kwargs)
@@ -496,6 +552,7 @@ class JsonInfo(object):
 
     Provides common utility methods to read json variables
     """
+
     def __init__(self):
         self._json_data = {}
 
@@ -536,6 +593,7 @@ class JsonInfo(object):
 
 class VariableInfo(JsonInfo):
     """Class to read and store variable information."""
+
     def __init__(self, table_type, short_name):
         """Class to read and store variable information.
 
@@ -647,6 +705,7 @@ class VariableInfo(JsonInfo):
 
 class CoordinateInfo(JsonInfo):
     """Class to read and store coordinate information."""
+
     def __init__(self, name):
         """Class to read and store coordinate information.
 
@@ -734,6 +793,7 @@ class CMIP5Info(InfoBase):
         If False, will look for a variable in other tables if it can not be
         found in the requested one
     """
+
     def __init__(self,
                  cmor_tables_path,
                  default=None,
@@ -892,6 +952,7 @@ class CMIP3Info(CMIP5Info):
         If False, will look for a variable in other tables if it can not be
         found in the requested one
     """
+
     def _read_table_file(self, table_file, table=None):
         for dim in ('zlevel', ):
             coord = CoordinateInfo(dim)
diff --git a/esmvalcore/preprocessor/_time.py b/esmvalcore/preprocessor/_time.py
index 265004a8c..e49afd478 100644
--- a/esmvalcore/preprocessor/_time.py
+++ b/esmvalcore/preprocessor/_time.py
@@ -22,7 +22,7 @@ from iris.coords import AuxCoord
 from iris.cube import Cube, CubeList
 from iris.time import PartialDateTime
 
-from esmvalcore.cmor.check import _get_next_month, _get_time_bounds
+from esmvalcore.cmor._fixes.fix import get_next_month, get_time_bounds
 from esmvalcore.iris_helpers import date2num
 
 from ._shared import get_iris_analysis_operation, operator_accept_weights
@@ -128,7 +128,7 @@ def _parse_end_date(date):
         if len(date) == 4:
             end_date = datetime.datetime(int(date) + 1, 1, 1, 0, 0, 0)
         elif len(date) == 6:
-            month, year = _get_next_month(int(date[4:]), int(date[0:4]))
+            month, year = get_next_month(int(date[4:]), int(date[0:4]))
             end_date = datetime.datetime(year, month, 1, 0, 0, 0)
         else:
             try:
@@ -966,7 +966,7 @@ def regrid_time(cube: Cube, frequency: str) -> Cube:
 
     # uniformize bounds
     cube.coord('time').bounds = None
-    cube.coord('time').bounds = _get_time_bounds(cube.coord('time'), frequency)
+    cube.coord('time').bounds = get_time_bounds(cube.coord('time'), frequency)
 
     # remove aux coords that will differ
     reset_aux = ['day_of_month', 'day_of_year']


Test patch:
diff --git a/tests/integration/cmor/_fixes/cesm/test_cesm2.py b/tests/integration/cmor/_fixes/cesm/test_cesm2.py
index 44fed835b..661eadcfb 100644
--- a/tests/integration/cmor/_fixes/cesm/test_cesm2.py
+++ b/tests/integration/cmor/_fixes/cesm/test_cesm2.py
@@ -7,6 +7,7 @@ from iris.coords import CellMethod, DimCoord
 from iris.cube import Cube, CubeList
 
 import esmvalcore.cmor._fixes.cesm.cesm2
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import CoordinateInfo, get_var_info
 from esmvalcore.config._config import get_extra_facets
@@ -338,6 +339,7 @@ def test_get_tas_fix():
     fix = Fix.get_fixes('CESM', 'CESM2', 'Amon', 'tas')
     assert fix == [
         esmvalcore.cmor._fixes.cesm.cesm2.AllVars(None),
+        GenericFix(None),
     ]
 
 
diff --git a/tests/integration/cmor/_fixes/cmip5/test_access1_0.py b/tests/integration/cmor/_fixes/cmip5/test_access1_0.py
index 1312762aa..3cba189b7 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_access1_0.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_access1_0.py
@@ -8,7 +8,7 @@ from iris.coords import AuxCoord, DimCoord
 from iris.cube import Cube, CubeList
 
 from esmvalcore.cmor._fixes.cmip5.access1_0 import AllVars, Cl
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 from esmvalcore.cmor.table import get_var_info
 from esmvalcore.iris_helpers import date2num
 
@@ -39,8 +39,9 @@ class TestAllVars:
     @staticmethod
     def test_get():
         """Test getting of fix."""
-        assert (Fix.get_fixes('CMIP5', 'ACCESS1-0', 'Amon', 'tas')
-                == [AllVars(None)])
+        assert Fix.get_fixes('CMIP5', 'ACCESS1-0', 'Amon', 'tas') == [
+            AllVars(None),  GenericFix(None)
+        ]
 
     @staticmethod
     def test_fix_metadata(cube):
@@ -66,7 +67,7 @@ class TestAllVars:
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'ACCESS1-0', 'Amon', 'cl')
-    assert fix == [Cl(None), AllVars(None)]
+    assert fix == [Cl(None), AllVars(None), GenericFix(None)]
 
 
 @pytest.fixture
diff --git a/tests/integration/cmor/_fixes/cmip5/test_access1_3.py b/tests/integration/cmor/_fixes/cmip5/test_access1_3.py
index 68d59201d..684e88f65 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_access1_3.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_access1_3.py
@@ -8,7 +8,7 @@ from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.access1_0 import Cl as BaseCl
 from esmvalcore.cmor._fixes.cmip5.access1_3 import AllVars, Cl
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 from esmvalcore.iris_helpers import date2num
 
 
@@ -38,8 +38,9 @@ class TestAllVars:
     @staticmethod
     def test_get():
         """Test getting of fix."""
-        assert (Fix.get_fixes('CMIP5', 'ACCESS1-3', 'Amon', 'tas')
-                == [AllVars(None)])
+        assert Fix.get_fixes('CMIP5', 'ACCESS1-3', 'Amon', 'tas') == [
+             AllVars(None), GenericFix(None)
+        ]
 
     @staticmethod
     def test_fix_metadata(cube):
@@ -65,7 +66,7 @@ class TestAllVars:
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'ACCESS1-3', 'Amon', 'cl')
-    assert fix == [Cl(None), AllVars(None)]
+    assert fix == [Cl(None), AllVars(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py
index 48d846146..65f37766e 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py
@@ -1,5 +1,4 @@
 """Test bcc-csm1-1 fixes."""
-import unittest
 
 import iris
 import numpy as np
@@ -9,6 +8,7 @@ from esmvalcore.cmor._fixes.common import (
     ClFixHybridPressureCoord,
     OceanFixGrid,
 )
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -16,7 +16,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'bcc-csm1-1', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -24,13 +24,10 @@ def test_cl_fix():
     assert Cl is ClFixHybridPressureCoord
 
 
-class TestTos(unittest.TestCase):
-    """Test tos fixes."""
-
-    def test_get(self):
-        """Test fix get."""
-        self.assertListEqual(
-            Fix.get_fixes('CMIP5', 'bcc-csm1-1', 'Amon', 'tos'), [Tos(None)])
+def test_get_tos_fix():
+    """Test getting of fix."""
+    fix = Fix.get_fixes('CMIP5', 'bcc-csm1-1', 'Omon', 'tos')
+    assert fix == [Tos(None), GenericFix(None)]
 
 
 def test_tos_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py
index 8473c84f4..8fe017fe9 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py
@@ -1,18 +1,17 @@
 """Test fixes for bcc-csm1-1-m."""
-import unittest
 
 from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1_m import Cl, Tos
 from esmvalcore.cmor._fixes.common import (
     ClFixHybridPressureCoord,
     OceanFixGrid,
 )
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'bcc-csm1-1-m', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -20,13 +19,10 @@ def test_cl_fix():
     assert Cl is ClFixHybridPressureCoord
 
 
-class TestTos(unittest.TestCase):
-    """Test tos fixes."""
-
-    def test_get(self):
-        """Test fix get."""
-        self.assertListEqual(
-            Fix.get_fixes('CMIP5', 'bcc-csm1-1-m', 'Amon', 'tos'), [Tos(None)])
+def test_get_tos_fix():
+    """Test getting of fix."""
+    fix = Fix.get_fixes('CMIP5', 'bcc-csm1-1-m', 'Omon', 'tos')
+    assert fix == [Tos(None), GenericFix(None)]
 
 
 def test_tos_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_bnu_esm.py b/tests/integration/cmor/_fixes/cmip5/test_bnu_esm.py
index e8045902f..d988e0244 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_bnu_esm.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_bnu_esm.py
@@ -5,9 +5,16 @@ import numpy.ma as ma
 from cf_units import Unit
 from iris.cube import Cube
 
-from esmvalcore.cmor._fixes.cmip5.bnu_esm import (Ch4, Cl, Co2, FgCo2,
-                                                  Od550Aer, SpCo2)
+from esmvalcore.cmor._fixes.cmip5.bnu_esm import (
+    Ch4,
+    Cl,
+    Co2,
+    FgCo2,
+    Od550Aer,
+    SpCo2,
+)
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -22,7 +29,7 @@ class TestCl(unittest.TestCase):
     def test_get(self):
         """Test fix get"""
         fix = Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'cl')
-        assert fix == [Cl(None)]
+        assert fix == [Cl(None), GenericFix(None)]
 
     def test_cl_fix(self):
         """Test fix for ``cl``."""
@@ -46,7 +53,7 @@ class TestCo2(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'co2'),
-                             [Co2(self.vardef)])
+                             [Co2(self.vardef), GenericFix(None)])
 
     def test_fix_metadata(self):
         """Test unit change."""
@@ -74,7 +81,7 @@ class Testfgco2(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'fgco2'),
-            [FgCo2(self.vardef)])
+            [FgCo2(self.vardef), GenericFix(None)])
 
     def test_fix_metadata(self):
         """Test unit fix."""
@@ -101,7 +108,7 @@ class TestCh4(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'ch4'),
-                             [Ch4(self.vardef)])
+                             [Ch4(self.vardef), GenericFix(None)])
 
     def test_fix_metadata(self):
         """Test unit fix."""
@@ -127,7 +134,8 @@ class Testspco2(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(
-            Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'spco2'), [SpCo2(None)])
+            Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'spco2'),
+            [SpCo2(None), GenericFix(None)])
 
     def test_fix_metadata(self):
         """Test fix."""
@@ -157,7 +165,7 @@ class TestOd550Aer(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'od550aer'),
-            [Od550Aer(None)])
+            [Od550Aer(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_canesm2.py b/tests/integration/cmor/_fixes/cmip5/test_canesm2.py
index 27d29839a..4e8f9c826 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_canesm2.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_canesm2.py
@@ -6,13 +6,14 @@ from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.canesm2 import Cl, FgCo2
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'CanESM2', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -31,7 +32,8 @@ class TestCanESM2Fgco2(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(
-            Fix.get_fixes('CMIP5', 'CANESM2', 'Amon', 'fgco2'), [FgCo2(None)])
+            Fix.get_fixes('CMIP5', 'CANESM2', 'Amon', 'fgco2'),
+            [FgCo2(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py b/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py
index f19cc9816..05bb6620a 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py
@@ -7,6 +7,7 @@ from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.bnu_esm import Cl as BaseCl
 from esmvalcore.cmor._fixes.cmip5.ccsm4 import AllVars, Cl, Csoil, So
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -59,7 +60,7 @@ class TestsAllVars(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'CCSM4', 'Amon', 'rlut'),
-                             [AllVars(None)])
+                             [AllVars(None), GenericFix(None)])
 
     def test_fix_metadata(self):
         """Check that latitudes values are rounded."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_cesm1_bgc.py b/tests/integration/cmor/_fixes/cmip5/test_cesm1_bgc.py
index 31fb3f670..fdb2314d8 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_cesm1_bgc.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_cesm1_bgc.py
@@ -6,13 +6,14 @@ from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.cesm1_bgc import Cl, Gpp, Nbp
 from esmvalcore.cmor._fixes.cmip5.cesm1_cam5 import Cl as BaseCl
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'CESM1-BGC', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -31,7 +32,8 @@ class TestGpp(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(
-            Fix.get_fixes('CMIP5', 'CESM1-BGC', 'Amon', 'gpp'), [Gpp(None)])
+            Fix.get_fixes('CMIP5', 'CESM1-BGC', 'Amon', 'gpp'),
+            [Gpp(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test fix to set missing values correctly."""
@@ -54,4 +56,5 @@ class TestNbp(TestGpp):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(
-            Fix.get_fixes('CMIP5', 'CESM1-BGC', 'Amon', 'nbp'), [Nbp(None)])
+            Fix.get_fixes('CMIP5', 'CESM1-BGC', 'Amon', 'nbp'),
+            [Nbp(None), GenericFix(None)])
diff --git a/tests/integration/cmor/_fixes/cmip5/test_cesm1_cam5.py b/tests/integration/cmor/_fixes/cmip5/test_cesm1_cam5.py
index d5991ddff..07cf8ab1a 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_cesm1_cam5.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_cesm1_cam5.py
@@ -3,13 +3,14 @@ import iris
 import pytest
 
 from esmvalcore.cmor._fixes.cmip5.cesm1_cam5 import Cl
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'CESM1-CAM5', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 @pytest.fixture
diff --git a/tests/integration/cmor/_fixes/cmip5/test_cesm1_fastchem.py b/tests/integration/cmor/_fixes/cmip5/test_cesm1_fastchem.py
index a41d1996f..6ae2a4491 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_cesm1_fastchem.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_cesm1_fastchem.py
@@ -1,13 +1,14 @@
 """Tests for CESM1-FASTCHEM fixes."""
 from esmvalcore.cmor._fixes.cmip5.cesm1_cam5 import Cl as BaseCl
 from esmvalcore.cmor._fixes.cmip5.cesm1_fastchem import Cl
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'CESM1-FASTCHEM', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_cesm1_waccm.py b/tests/integration/cmor/_fixes/cmip5/test_cesm1_waccm.py
index 20023eebf..146a456da 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_cesm1_waccm.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_cesm1_waccm.py
@@ -1,13 +1,14 @@
 """Tests for CESM1-WACCM fixes."""
 from esmvalcore.cmor._fixes.cmip5.cesm1_cam5 import Cl as BaseCl
 from esmvalcore.cmor._fixes.cmip5.cesm1_waccm import Cl
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'CESM1-WACCM', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_cnrm_cm5.py b/tests/integration/cmor/_fixes/cmip5/test_cnrm_cm5.py
index ae9da2ef0..ce6ebe012 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_cnrm_cm5.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_cnrm_cm5.py
@@ -4,8 +4,9 @@ import unittest
 from cf_units import Unit
 from iris.cube import Cube
 
-from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor._fixes.cmip5.cnrm_cm5 import Msftmyz, Msftmyzba
+from esmvalcore.cmor._fixes.fix import GenericFix
+from esmvalcore.cmor.fix import Fix
 
 
 class TestMsftmyz(unittest.TestCase):
@@ -19,7 +20,7 @@ class TestMsftmyz(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'CNRM-CM5', 'Amon', 'msftmyz'),
-            [Msftmyz(None)])
+            [Msftmyz(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -39,7 +40,7 @@ class TestMsftmyzba(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'CNRM-CM5', 'Amon', 'msftmyzba'),
-            [Msftmyzba(None)])
+            [Msftmyzba(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py b/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py
index a7d23d9d4..3fa4318d7 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py
@@ -1,13 +1,13 @@
 """Test fixes for CSIRO-Mk3-6-0."""
 from esmvalcore.cmor._fixes.cmip5.csiro_mk3_6_0 import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'CSIRO-Mk3-6-0', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_ec_earth.py b/tests/integration/cmor/_fixes/cmip5/test_ec_earth.py
index 8ec734a90..4b480ef81 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_ec_earth.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_ec_earth.py
@@ -2,18 +2,19 @@
 import unittest
 
 import numpy as np
-
 from cf_units import Unit
 from iris.coords import AuxCoord, DimCoord
 from iris.cube import Cube, CubeList
 from iris.exceptions import CoordinateNotFoundError
+
 from esmvalcore.cmor._fixes.cmip5.ec_earth import (
     Areacello,
     Pr,
     Sftlf,
     Sic,
     Tas,
-    )
+)
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -27,7 +28,7 @@ class TestSic(unittest.TestCase):
     def test_get(self):
         """Test fix get"""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'EC-EARTH', 'Amon', 'sic'),
-                             [Sic(None)])
+                             [Sic(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -46,7 +47,8 @@ class TestSftlf(unittest.TestCase):
     def test_get(self):
         """Test fix get"""
         self.assertListEqual(
-            Fix.get_fixes('CMIP5', 'EC-EARTH', 'Amon', 'sftlf'), [Sftlf(None)])
+            Fix.get_fixes('CMIP5', 'EC-EARTH', 'Amon', 'sftlf'),
+            [Sftlf(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -90,7 +92,7 @@ class TestTas(unittest.TestCase):
     def test_get(self):
         """Test fix get"""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'EC-EARTH', 'Amon', 'tas'),
-                             [Tas(None)])
+                             [Tas(None), GenericFix(None)])
 
     def test_tas_fix_metadata(self):
         """Test metadata fix."""
@@ -148,7 +150,7 @@ class TestAreacello(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'EC-EARTH', 'Omon', 'areacello'),
-            [Areacello(None)],
+            [Areacello(None), GenericFix(None)],
         )
 
     def test_areacello_fix_metadata(self):
@@ -210,7 +212,7 @@ class TestPr(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'EC-EARTH', 'Amon', 'pr'),
-            [Pr(None)],
+            [Pr(None), GenericFix(None)],
         )
 
     def test_pr_fix_metadata(self):
diff --git a/tests/integration/cmor/_fixes/cmip5/test_fgoals_g2.py b/tests/integration/cmor/_fixes/cmip5/test_fgoals_g2.py
index abda3d180..b70b16fa2 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_fgoals_g2.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_fgoals_g2.py
@@ -5,6 +5,7 @@ from iris.coords import DimCoord
 from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.fgoals_g2 import AllVars
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -33,8 +34,9 @@ class TestAll:
     @staticmethod
     def test_get():
         """Test fix get."""
-        assert (Fix.get_fixes('CMIP5', 'FGOALS-G2', 'Amon', 'tas')
-                == [AllVars(None)])
+        assert Fix.get_fixes('CMIP5', 'FGOALS-G2', 'Amon', 'tas') == [
+             AllVars(None), GenericFix(None)
+        ]
 
     @staticmethod
     def test_fix_metadata(cube):
diff --git a/tests/integration/cmor/_fixes/cmip5/test_fgoals_s2.py b/tests/integration/cmor/_fixes/cmip5/test_fgoals_s2.py
index e77c095b3..a1c15afeb 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_fgoals_s2.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_fgoals_s2.py
@@ -4,13 +4,14 @@ from iris.coords import AuxCoord, DimCoord
 from iris.cube import Cube, CubeList
 
 from esmvalcore.cmor._fixes.cmip5.fgoals_s2 import AllVars
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_allvars_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'FGOALS-s2', 'Amon', 'tas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 LAT_COORD = DimCoord(
diff --git a/tests/integration/cmor/_fixes/cmip5/test_fio_esm.py b/tests/integration/cmor/_fixes/cmip5/test_fio_esm.py
index b8b8429f3..c0705e58f 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_fio_esm.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_fio_esm.py
@@ -4,15 +4,16 @@ import unittest
 from cf_units import Unit
 from iris.cube import Cube
 
-from esmvalcore.cmor.fix import Fix
-from esmvalcore.cmor._fixes.cmip5.fio_esm import Ch4, Cl, Co2
 from esmvalcore.cmor._fixes.cmip5.cesm1_cam5 import Cl as BaseCl
+from esmvalcore.cmor._fixes.cmip5.fio_esm import Ch4, Cl, Co2
+from esmvalcore.cmor._fixes.fix import GenericFix
+from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'FIO-ESM', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -30,7 +31,7 @@ class TestCh4(unittest.TestCase):
     def test_get(self):
         """Test fix get"""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'FIO-ESM', 'Amon', 'ch4'),
-                             [Ch4(None)])
+                             [Ch4(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -49,7 +50,7 @@ class TestCo2(unittest.TestCase):
     def test_get(self):
         """Test fix get"""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'FIO-ESM', 'Amon', 'co2'),
-                             [Co2(None)])
+                             [Co2(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_gfdl_cm2p1.py b/tests/integration/cmor/_fixes/cmip5/test_gfdl_cm2p1.py
index 63d5ce426..8a4fa19fd 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_gfdl_cm2p1.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_gfdl_cm2p1.py
@@ -6,9 +6,15 @@ import iris
 from cf_units import Unit
 from iris.cube import Cube
 
-from esmvalcore.cmor._fixes.cmip5.gfdl_cm2p1 import (AllVars, Areacello, Cl,
-                                                     Sftof, Sit)
 from esmvalcore.cmor._fixes.cmip5.cesm1_cam5 import Cl as BaseCl
+from esmvalcore.cmor._fixes.cmip5.gfdl_cm2p1 import (
+    AllVars,
+    Areacello,
+    Cl,
+    Sftof,
+    Sit,
+)
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -19,7 +25,7 @@ class TestCl(unittest.TestCase):
         """Test getting of fix."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-CM2P1', 'Amon', 'cl'),
-            [Cl(None), AllVars(None)])
+            [Cl(None), AllVars(None), GenericFix(None)])
 
     def test_fix(self):
         """Test fix for ``cl``."""
@@ -37,7 +43,7 @@ class TestSftof(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-CM2P1', 'fx', 'sftof'),
-            [Sftof(None), AllVars(None)])
+            [Sftof(None), AllVars(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -59,7 +65,9 @@ class TestAreacello(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-CM2P1', 'Amon', 'areacello'),
-            [Areacello(self.vardef), AllVars(self.vardef)])
+            [Areacello(self.vardef),
+             AllVars(self.vardef),
+             GenericFix(self.vardef)])
 
     def test_fix_metadata(self):
         """Test data fix."""
@@ -101,7 +109,7 @@ class TestSit(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-CM2P1', 'OImon', 'sit'),
-            [Sit(self.var_info_mock), AllVars(None)])
+            [Sit(self.var_info_mock), AllVars(None), GenericFix(None)])
 
     def test_fix_metadata_day_do_nothing(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_gfdl_cm3.py b/tests/integration/cmor/_fixes/cmip5/test_gfdl_cm3.py
index c4aaae272..bcda26c95 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_gfdl_cm3.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_gfdl_cm3.py
@@ -5,6 +5,7 @@ from cf_units import Unit
 from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.gfdl_cm3 import AllVars, Areacello, Sftof
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -20,7 +21,7 @@ class TestSftof(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-CM3', 'fx', 'sftof'),
-            [Sftof(None), AllVars(None)])
+            [Sftof(None), AllVars(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -41,7 +42,9 @@ class TestAreacello(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-CM3', 'Amon', 'areacello'),
-            [Areacello(self.vardef), AllVars(self.vardef)])
+            [Areacello(self.vardef),
+             AllVars(self.vardef),
+             GenericFix(self.vardef)])
 
     def test_fix_metadata(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_gfdl_esm2g.py b/tests/integration/cmor/_fixes/cmip5/test_gfdl_esm2g.py
index 89a1a1e32..d5189e84c 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_gfdl_esm2g.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_gfdl_esm2g.py
@@ -7,9 +7,16 @@ import pytest
 from cf_units import Unit
 from iris.cube import Cube
 
-from esmvalcore.cmor._fixes.cmip5.gfdl_esm2g import (AllVars, Areacello, Co2,
-                                                     FgCo2, Usi, Vsi,
-                                                     _get_and_remove)
+from esmvalcore.cmor._fixes.cmip5.gfdl_esm2g import (
+    AllVars,
+    Areacello,
+    Co2,
+    FgCo2,
+    Usi,
+    Vsi,
+    _get_and_remove,
+)
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -71,7 +78,7 @@ class TestCo2(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-ESM2G', 'Amon', 'co2'),
-            [Co2(None), AllVars(None)])
+            [Co2(None), AllVars(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -92,7 +99,9 @@ class TestUsi(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-ESM2G', 'day', 'usi'),
-            [Usi(self.vardef), AllVars(self.vardef)])
+            [Usi(self.vardef),
+             AllVars(self.vardef),
+             GenericFix(self.vardef)])
 
     def test_fix_data(self):
         """Test metadata fix."""
@@ -112,7 +121,9 @@ class TestVsi(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-ESM2G', 'day', 'vsi'),
-            [Vsi(self.vardef), AllVars(self.vardef)])
+            [Vsi(self.vardef),
+             AllVars(self.vardef),
+             GenericFix(self.vardef)])
 
     def test_fix_data(self):
         """Test metadata fix."""
@@ -132,7 +143,9 @@ class TestAreacello(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-ESM2G', 'fx', 'areacello'),
-            [Areacello(self.vardef), AllVars(self.vardef)])
+            [Areacello(self.vardef),
+             AllVars(self.vardef),
+             GenericFix(self.vardef)])
 
     def test_fix_metadata(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_gfdl_esm2m.py b/tests/integration/cmor/_fixes/cmip5/test_gfdl_esm2m.py
index 1538098d2..1897ba5c8 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_gfdl_esm2m.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_gfdl_esm2m.py
@@ -4,8 +4,13 @@ import unittest
 from cf_units import Unit
 from iris.cube import Cube
 
-from esmvalcore.cmor._fixes.cmip5.gfdl_esm2m import (AllVars, Areacello, Co2,
-                                                     Sftof)
+from esmvalcore.cmor._fixes.cmip5.gfdl_esm2m import (
+    AllVars,
+    Areacello,
+    Co2,
+    Sftof,
+)
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -21,7 +26,7 @@ class TestSftof(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-ESM2M', 'fx', 'sftof'),
-            [Sftof(None), AllVars(None)])
+            [Sftof(None), AllVars(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -41,7 +46,7 @@ class TestCo2(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-ESM2M', 'Amon', 'co2'),
-            [Co2(None), AllVars(None)])
+            [Co2(None), AllVars(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -62,7 +67,9 @@ class TestAreacello(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'GFDL-ESM2M', 'fx', 'areacello'),
-            [Areacello(self.vardef), AllVars(self.vardef)])
+            [Areacello(self.vardef),
+             AllVars(self.vardef),
+             GenericFix(self.vardef)])
 
     def test_fix_metadata(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py
index 5fd78f648..9c39dd136 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py
@@ -1,13 +1,13 @@
 """Test fixes for GISS-E2-H."""
 from esmvalcore.cmor._fixes.cmip5.giss_e2_h import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'GISS-E2-H', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py
index e030bb4aa..d95b517cd 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py
@@ -1,13 +1,13 @@
 """Test fixes for GISS-E2-R."""
 from esmvalcore.cmor._fixes.cmip5.giss_e2_r import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'GISS-E2-R', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_hadgem2_cc.py b/tests/integration/cmor/_fixes/cmip5/test_hadgem2_cc.py
index ed2f7c0d1..0e6a4fc57 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_hadgem2_cc.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_hadgem2_cc.py
@@ -1,8 +1,9 @@
 """Test HADGEM2-CC fixes."""
 import unittest
 
+from esmvalcore.cmor._fixes.cmip5.hadgem2_cc import O2, AllVars
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
-from esmvalcore.cmor._fixes.cmip5.hadgem2_cc import AllVars, O2
 
 
 class TestAllVars(unittest.TestCase):
@@ -11,7 +12,7 @@ class TestAllVars(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'HADGEM2-CC', 'Amon', 'tas'),
-            [AllVars(None)])
+            [AllVars(None), GenericFix(None)])
 
 
 class TestO2(unittest.TestCase):
@@ -20,4 +21,4 @@ class TestO2(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'HADGEM2-CC', 'Amon', 'o2'),
-            [O2(None), AllVars(None)])
+            [O2(None), AllVars(None), GenericFix(None)])
diff --git a/tests/integration/cmor/_fixes/cmip5/test_hadgem2_es.py b/tests/integration/cmor/_fixes/cmip5/test_hadgem2_es.py
index 99c7eb4aa..c0ab2bb8f 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_hadgem2_es.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_hadgem2_es.py
@@ -3,6 +3,7 @@ import unittest
 
 from esmvalcore.cmor._fixes.cmip5.hadgem2_es import O2, AllVars, Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridHeightCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -13,7 +14,7 @@ class TestAllVars(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'HADGEM2-ES', 'Amon', 'tas'),
-            [AllVars(None)])
+            [AllVars(None), GenericFix(None)])
 
 
 class TestO2(unittest.TestCase):
@@ -23,13 +24,13 @@ class TestO2(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'HADGEM2-ES', 'Amon', 'o2'),
-            [O2(None), AllVars(None)])
+            [O2(None), AllVars(None), GenericFix(None)])
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'HadGEM2-ES', 'Amon', 'cl')
-    assert fix == [Cl(None), AllVars(None)]
+    assert fix == [Cl(None), AllVars(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py b/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py
index a5a5cf023..dcabbbf31 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py
@@ -6,13 +6,14 @@ from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.inmcm4 import Cl, Gpp, Lai, Nbp
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'inmcm4', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -31,7 +32,7 @@ class TestGpp(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'INMCM4', 'Amon', 'gpp'),
-                             [Gpp(None)])
+                             [Gpp(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fox."""
@@ -51,7 +52,7 @@ class TestLai(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'INMCM4', 'Amon', 'lai'),
-                             [Lai(None)])
+                             [Lai(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -71,7 +72,7 @@ class TestNbp(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'INMCM4', 'Amon', 'nbp'),
-                             [Nbp(None)])
+                             [Nbp(None), GenericFix(None)])
 
     def test_fix_metadata(self):
         """Test fix on nbp files to set standard_name."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py
index 443a4aafb..67f50c1ea 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py
@@ -1,13 +1,13 @@
 """Test fixes for IPSL-CM5A-LR."""
 from esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_lr import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'IPSL-CM5A-LR', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py
index ff3e0adc2..787a2d804 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py
@@ -1,13 +1,13 @@
 """Test fixes for IPSL-CM5A-MR."""
 from esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_mr import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'IPSL-CM5A-MR', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py
index e04fa4718..4a7b0a447 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py
@@ -1,13 +1,13 @@
 """Test fixes for IPSL-CM5B-LR."""
 from esmvalcore.cmor._fixes.cmip5.ipsl_cm5b_lr import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'IPSL-CM5B-LR', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_miroc5.py b/tests/integration/cmor/_fixes/cmip5/test_miroc5.py
index a9d61dde1..63a706eef 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_miroc5.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_miroc5.py
@@ -7,13 +7,14 @@ from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.miroc5 import Cl, Hur, Pr, Sftof, Tas
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -24,13 +25,13 @@ def test_cl_fix():
 def test_get_hur_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'hur')
-    assert fix == [Hur(None)]
+    assert fix == [Hur(None), GenericFix(None)]
 
 
 def test_get_pr_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'pr')
-    assert fix == [Pr(None)]
+    assert fix == [Pr(None), GenericFix(None)]
 
 
 @unittest.mock.patch(
@@ -64,7 +65,7 @@ class TestSftof(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'sftof'),
-                             [Sftof(None)])
+                             [Sftof(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -88,7 +89,7 @@ class TestTas(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'tas'),
-                             [Tas(None)])
+                             [Tas(None), GenericFix(None)])
 
     def test_fix_metadata(self):
         """Test metadata fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py b/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py
index f87902a8d..1010e4e67 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py
@@ -9,6 +9,7 @@ from iris.exceptions import CoordinateNotFoundError
 
 from esmvalcore.cmor._fixes.cmip5.miroc_esm import AllVars, Cl, Co2, Tro3
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -16,7 +17,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'MIROC-ESM', 'Amon', 'cl')
-    assert fix == [Cl(None), AllVars(None)]
+    assert fix == [Cl(None), AllVars(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -37,7 +38,9 @@ class TestCo2(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'MIROC-ESM', 'Amon', 'co2'),
-            [Co2(self.vardef), AllVars(self.vardef)])
+            [Co2(self.vardef),
+             AllVars(self.vardef),
+             GenericFix(self.vardef)])
 
     def test_fix_metadata(self):
         """Test unit fix."""
@@ -58,7 +61,7 @@ class TestTro3(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'MIROC-ESM', 'Amon', 'tro3'),
-            [Tro3(None), AllVars(None)])
+            [Tro3(None), AllVars(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -102,7 +105,7 @@ class TestAll(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'MIROC-ESM', 'Amon', 'tos'),
-            [AllVars(None)])
+            [AllVars(None), GenericFix(None)])
 
     def test_fix_metadata_plev(self):
         """Test plev fix."""
@@ -112,7 +115,7 @@ class TestAll(unittest.TestCase):
         cube.coord('air_pressure')
 
     def test_fix_metadata_no_plev(self):
-        """Test plev fix wotk with no plev."""
+        """Test plev fix work with no plev."""
         self.cube.remove_coord('AR5PL35')
         cube = self.fix.fix_metadata([self.cube])[0]
         with self.assertRaises(CoordinateNotFoundError):
diff --git a/tests/integration/cmor/_fixes/cmip5/test_miroc_esm_chem.py b/tests/integration/cmor/_fixes/cmip5/test_miroc_esm_chem.py
index 186f32171..1e14dfd30 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_miroc_esm_chem.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_miroc_esm_chem.py
@@ -5,8 +5,9 @@ import unittest
 from cf_units import Unit
 from iris.cube import Cube
 
-from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor._fixes.cmip5.miroc_esm_chem import Tro3
+from esmvalcore.cmor._fixes.fix import GenericFix
+from esmvalcore.cmor.fix import Fix
 
 
 class TestTro3(unittest.TestCase):
@@ -20,7 +21,7 @@ class TestTro3(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'MIROC-ESM-CHEM', 'Amon', 'tro3'),
-            [Tro3(None)])
+            [Tro3(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py
index effb781f9..07aa974d1 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py
@@ -6,13 +6,14 @@ from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.mpi_esm_lr import Cl, Pctisccp
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'MPI-ESM-LR', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -32,7 +33,7 @@ class TestPctisccp2(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'MPI-ESM-LR', 'Amon', 'pctisccp'),
-            [Pctisccp(None)])
+            [Pctisccp(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py
index 1c074d41a..96cd83b96 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py
@@ -1,13 +1,13 @@
 """Test fixes for MPI-ESM-MR."""
 from esmvalcore.cmor._fixes.cmip5.mpi_esm_mr import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'MPI-ESM-MR', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py
index 7e0aa87a5..2466e5a9f 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py
@@ -1,13 +1,13 @@
 """Test fixes for MPI-ESM-P."""
 from esmvalcore.cmor._fixes.cmip5.mpi_esm_p import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'MPI-ESM-P', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py b/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py
index 2b0d85ab7..717c66cee 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py
@@ -3,13 +3,14 @@ import unittest
 
 from esmvalcore.cmor._fixes.cmip5.mri_cgcm3 import Cl, Msftmyz, ThetaO
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'MRI-CGCM3', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -24,7 +25,7 @@ class TestMsftmyz(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'MRI-CGCM3', 'Amon', 'msftmyz'),
-            [Msftmyz(None)])
+            [Msftmyz(None), GenericFix(None)])
 
 
 class TestThetao(unittest.TestCase):
@@ -34,4 +35,4 @@ class TestThetao(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'MRI-CGCM3', 'Amon', 'thetao'),
-            [ThetaO(None)])
+            [ThetaO(None), GenericFix(None)])
diff --git a/tests/integration/cmor/_fixes/cmip5/test_mri_esm1.py b/tests/integration/cmor/_fixes/cmip5/test_mri_esm1.py
index c00af1d6f..f4d39eb70 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_mri_esm1.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_mri_esm1.py
@@ -1,8 +1,9 @@
 """Test MRI-ESM1 fixes."""
 import unittest
 
-from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor._fixes.cmip5.mri_esm1 import Msftmyz
+from esmvalcore.cmor._fixes.fix import GenericFix
+from esmvalcore.cmor.fix import Fix
 
 
 class TestMsftmyz(unittest.TestCase):
@@ -11,4 +12,4 @@ class TestMsftmyz(unittest.TestCase):
         """Test fix get"""
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'MRI-ESM1', 'Amon', 'msftmyz'),
-            [Msftmyz(None)])
+            [Msftmyz(None), GenericFix(None)])
diff --git a/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py b/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py
index 79664ce6b..1ad165c63 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py
@@ -1,13 +1,13 @@
 """Test fixes for NorESM1-M."""
 from esmvalcore.cmor._fixes.cmip5.noresm1_m import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP5', 'NorESM1-M', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip5/test_noresm1_me.py b/tests/integration/cmor/_fixes/cmip5/test_noresm1_me.py
index ab286d78b..b600311d5 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_noresm1_me.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_noresm1_me.py
@@ -1,10 +1,11 @@
 """Tests for fixes of NorESM1-ME (CMIP5)."""
-import pytest
 import iris
+import pytest
 from iris.cube import CubeList
 
-from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor._fixes.cmip5.noresm1_me import Tas
+from esmvalcore.cmor._fixes.fix import GenericFix
+from esmvalcore.cmor.fix import Fix
 
 DIM_COORD_SHORT = iris.coords.DimCoord(
     [1.0, 2.0, 3.0],
@@ -69,4 +70,6 @@ def test_tas(cubes_in, cubes_out):
 
 def test_get():
     """Test fix get"""
-    assert Fix.get_fixes('CMIP5', 'NORESM1-ME', 'Amon', 'tas') == [Tas(None)]
+    assert Fix.get_fixes('CMIP5', 'NORESM1-ME', 'Amon', 'tas') == [
+        Tas(None), GenericFix(None)
+    ]
diff --git a/tests/integration/cmor/_fixes/cmip6/test_access_cm2.py b/tests/integration/cmor/_fixes/cmip6/test_access_cm2.py
index 0e5bd66eb..00977403e 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_access_cm2.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_access_cm2.py
@@ -7,6 +7,7 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.access_cm2 import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridHeightCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 B_POINTS = [
@@ -108,7 +109,7 @@ def cl_cubes():
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ACCESS-CM2', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -132,7 +133,7 @@ def test_cl_fix_metadata(mock_base_fix_metadata, cl_cubes):
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ACCESS-CM2', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -143,7 +144,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ACCESS-CM2', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_access_esm1_5.py b/tests/integration/cmor/_fixes/cmip6/test_access_esm1_5.py
index d126d3a74..29fb72c87 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_access_esm1_5.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_access_esm1_5.py
@@ -7,6 +7,7 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.access_esm1_5 import Cl, Cli, Clw, Hus, Zg
 from esmvalcore.cmor._fixes.common import ClFixHybridHeightCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -77,7 +78,7 @@ def cl_cubes():
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ACCESS-ESM1-5', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 @unittest.mock.patch(
@@ -101,7 +102,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ACCESS-ESM1-5', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -112,7 +113,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ACCESS-ESM1-5', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -143,7 +144,7 @@ def cubes_with_wrong_air_pressure():
 def test_get_hus_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ACCESS-ESM1-5', 'Amon', 'hus')
-    assert fix == [Hus(None)]
+    assert fix == [Hus(None), GenericFix(None)]
 
 
 def test_hus_fix_metadata(cubes_with_wrong_air_pressure):
@@ -169,7 +170,7 @@ def test_hus_fix_metadata(cubes_with_wrong_air_pressure):
 def test_get_zg_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ACCESS-ESM1-5', 'Amon', 'zg')
-    assert fix == [Zg(None)]
+    assert fix == [Zg(None), GenericFix(None)]
 
 
 def test_zg_fix_metadata(cubes_with_wrong_air_pressure):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_awi_cm_1_1_mr.py b/tests/integration/cmor/_fixes/cmip6/test_awi_cm_1_1_mr.py
index f198e2f69..780b09bd7 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_awi_cm_1_1_mr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_awi_cm_1_1_mr.py
@@ -3,6 +3,7 @@ import iris
 import pytest
 
 from esmvalcore.cmor._fixes.cmip6.awi_cm_1_1_mr import AllVars
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -34,7 +35,7 @@ def cubes():
 
 def test_get_allvars_fix():
     fix = Fix.get_fixes('CMIP6', 'AWI-CM-1-1-MR', 'Amon', 'wrong_lat_lname')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_metadata(cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_awi_esm_1_1_lr.py b/tests/integration/cmor/_fixes/cmip6/test_awi_esm_1_1_lr.py
index eba75631e..07dd1f41e 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_awi_esm_1_1_lr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_awi_esm_1_1_lr.py
@@ -3,6 +3,7 @@ import iris
 import pytest
 
 from esmvalcore.cmor._fixes.cmip6.awi_esm_1_1_lr import AllVars
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -15,7 +16,7 @@ def sample_cubes():
 
 def test_get_tas_fix():
     fix = Fix.get_fixes('CMIP6', 'AWI-ESM-1-1-LR', 'Amon', 'tas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_metadata(sample_cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py
index ee5739380..5281953fa 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py
@@ -12,13 +12,13 @@ from esmvalcore.cmor._fixes.common import (
     ClFixHybridPressureCoord,
     OceanFixGrid,
 )
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_areacello_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Amon', 'areacello')
-    assert fix == [Areacello(None)]
+    assert fix == [Areacello(None), GenericFix(None)]
 
 
 def test_areacello_fix():
@@ -29,7 +29,7 @@ def test_areacello_fix():
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -40,7 +40,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -51,7 +51,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -62,7 +62,7 @@ def test_clw_fix():
 def test_get_tos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Omon', 'tos')
-    assert fix == [Tos(None)]
+    assert fix == [Tos(None), GenericFix(None)]
 
 
 def test_tos_fix():
@@ -73,7 +73,7 @@ def test_tos_fix():
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
@@ -84,7 +84,7 @@ def test_siconc_fix():
 def test_get_sos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Omon', 'sos')
-    assert fix == [Sos(None)]
+    assert fix == [Sos(None), GenericFix(None)]
 
 
 def test_sos_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py
index 382d0268f..076390e92 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py
@@ -11,13 +11,13 @@ from esmvalcore.cmor._fixes.common import (
     ClFixHybridPressureCoord,
     OceanFixGrid,
 )
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -28,7 +28,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -39,7 +39,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -50,7 +50,7 @@ def test_clw_fix():
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
@@ -61,7 +61,7 @@ def test_siconc_fix():
 def test_get_sos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Omon', 'sos')
-    assert fix == [Sos(None)]
+    assert fix == [Sos(None), GenericFix(None)]
 
 
 def test_sos_fix():
@@ -72,7 +72,7 @@ def test_sos_fix():
 def test_get_tos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Omon', 'tos')
-    assert fix == [Tos(None)]
+    assert fix == [Tos(None), GenericFix(None)]
 
 
 def test_tos_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py b/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py
index 55cb512ae..eb2367539 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py
@@ -1,13 +1,13 @@
 """Test fixes for CAMS-CSM1-0."""
 from esmvalcore.cmor._fixes.cmip6.cams_csm1_0 import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CAMS-CSM1-0', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +18,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CAMS-CSM1-0', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +29,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CAMS-CSM1-0', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_canesm5.py b/tests/integration/cmor/_fixes/cmip6/test_canesm5.py
index 9b9638e5f..5f23af82f 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_canesm5.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_canesm5.py
@@ -4,13 +4,14 @@ import numpy as np
 import pytest
 
 from esmvalcore.cmor._fixes.cmip6.canesm5 import Co2, Gpp
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_co2_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CanESM5', 'Amon', 'co2')
-    assert fix == [Co2(None)]
+    assert fix == [Co2(None), GenericFix(None)]
 
 
 @pytest.fixture
@@ -48,7 +49,7 @@ def gpp_cube():
 def test_get_gpp_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CanESM5', 'Lmon', 'gpp')
-    assert fix == [Gpp(None)]
+    assert fix == [Gpp(None), GenericFix(None)]
 
 
 def test_gpp_fix_data(gpp_cube):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_canesm5_canoe.py b/tests/integration/cmor/_fixes/cmip6/test_canesm5_canoe.py
index 349444559..f0f43fd59 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_canesm5_canoe.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_canesm5_canoe.py
@@ -2,13 +2,13 @@
 from esmvalcore.cmor._fixes.cmip6.canesm5 import Co2 as BaseCo2
 from esmvalcore.cmor._fixes.cmip6.canesm5 import Gpp as BaseGpp
 from esmvalcore.cmor._fixes.cmip6.canesm5_canoe import Co2, Gpp
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_co2_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CanESM5-CanOE', 'Amon', 'co2')
-    assert fix == [Co2(None)]
+    assert fix == [Co2(None), GenericFix(None)]
 
 
 def test_co2_fix():
@@ -19,7 +19,7 @@ def test_co2_fix():
 def test_get_gpp_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CanESM5-CanOE', 'Lmon', 'gpp')
-    assert fix == [Gpp(None)]
+    assert fix == [Gpp(None), GenericFix(None)]
 
 
 def test_gpp_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cas_esm2_0.py b/tests/integration/cmor/_fixes/cmip6/test_cas_esm2_0.py
index bd018b3f2..2e22f91c1 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cas_esm2_0.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cas_esm2_0.py
@@ -1,13 +1,14 @@
 """Tests for the fixes of CAS-ESM2-0."""
 from esmvalcore.cmor._fixes.cmip6.cas_esm2_0 import Cl
 from esmvalcore.cmor._fixes.cmip6.ciesm import Cl as BaseCl
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CAS-ESM2-0', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cesm2.py b/tests/integration/cmor/_fixes/cmip6/test_cesm2.py
index 55442a323..e60e1727b 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cesm2.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cesm2.py
@@ -19,6 +19,7 @@ from esmvalcore.cmor._fixes.cmip6.cesm2 import (
     Tos,
 )
 from esmvalcore.cmor._fixes.common import SiconcFixScalarCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -26,7 +27,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 AIR_PRESSURE_POINTS = np.array([[[[1.0, 1.0, 1.0, 1.0],
@@ -163,7 +164,7 @@ def test_cl_fix_metadata(cl_cubes):
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -174,7 +175,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -268,31 +269,31 @@ def thetao_cubes():
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'Amon', 'tas')
-    assert fix == [Tas(None)]
+    assert fix == [Tas(None), GenericFix(None)]
 
 
 def test_get_tos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'tos')
-    assert fix == [Tos(None), Omon(None)]
+    assert fix == [Tos(None), Omon(None), GenericFix(None)]
 
 
 def test_get_thetao_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'thetao')
-    assert fix == [Omon(None)]
+    assert fix == [Omon(None), GenericFix(None)]
 
 
 def test_get_fgco2_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'fgco2')
-    assert fix == [Fgco2(None), Omon(None)]
+    assert fix == [Fgco2(None), Omon(None), GenericFix(None)]
 
 
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_tas_fix_metadata(tas_cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cesm2_fv2.py b/tests/integration/cmor/_fixes/cmip6/test_cesm2_fv2.py
index 96a201307..16d8a9c0b 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cesm2_fv2.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cesm2_fv2.py
@@ -12,13 +12,14 @@ from esmvalcore.cmor._fixes.cmip6.cesm2_fv2 import (
     Tas,
 )
 from esmvalcore.cmor._fixes.common import SiconcFixScalarCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-FV2', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -29,7 +30,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-FV2', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -40,7 +41,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-FV2', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -51,7 +52,7 @@ def test_clw_fix():
 def test_get_fgco2_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-FV2', 'Omon', 'fgco2')
-    assert fix == [Fgco2(None), Omon(None)]
+    assert fix == [Fgco2(None), Omon(None), GenericFix(None)]
 
 
 def test_fgco2_fix():
@@ -62,7 +63,7 @@ def test_fgco2_fix():
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-FV2', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
@@ -73,7 +74,7 @@ def test_siconc_fix():
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-FV2', 'Amon', 'tas')
-    assert fix == [Tas(None)]
+    assert fix == [Tas(None), GenericFix(None)]
 
 
 def test_tas_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cesm2_waccm.py b/tests/integration/cmor/_fixes/cmip6/test_cesm2_waccm.py
index b9075b671..b80beb45f 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cesm2_waccm.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cesm2_waccm.py
@@ -20,13 +20,14 @@ from esmvalcore.cmor._fixes.cmip6.cesm2_waccm import (
     Tas,
 )
 from esmvalcore.cmor._fixes.common import SiconcFixScalarCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -65,7 +66,7 @@ def test_cl_fix_file(mock_get_filepath, tmp_path, test_data_path):
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -76,7 +77,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -87,7 +88,7 @@ def test_clw_fix():
 def test_get_fgco2_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM', 'Omon', 'fgco2')
-    assert fix == [Fgco2(None), Omon(None)]
+    assert fix == [Fgco2(None), Omon(None), GenericFix(None)]
 
 
 def test_fgco2_fix():
@@ -98,7 +99,7 @@ def test_fgco2_fix():
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
@@ -117,7 +118,7 @@ def tas_cubes():
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM', 'Amon', 'tas')
-    assert fix == [Tas(None)]
+    assert fix == [Tas(None), GenericFix(None)]
 
 
 def test_tas_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cesm2_waccm_fv2.py b/tests/integration/cmor/_fixes/cmip6/test_cesm2_waccm_fv2.py
index a5ac8f673..d4eb9b4b6 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cesm2_waccm_fv2.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cesm2_waccm_fv2.py
@@ -1,6 +1,6 @@
 """Tests for the fixes of CESM2-WACCM-FV2."""
-from esmvalcore.cmor._fixes.cmip6.cesm2 import Tas as BaseTas
 from esmvalcore.cmor._fixes.cmip6.cesm2 import Fgco2 as BaseFgco2
+from esmvalcore.cmor._fixes.cmip6.cesm2 import Tas as BaseTas
 from esmvalcore.cmor._fixes.cmip6.cesm2_waccm import Cl as BaseCl
 from esmvalcore.cmor._fixes.cmip6.cesm2_waccm_fv2 import (
     Cl,
@@ -12,13 +12,14 @@ from esmvalcore.cmor._fixes.cmip6.cesm2_waccm_fv2 import (
     Tas,
 )
 from esmvalcore.cmor._fixes.common import SiconcFixScalarCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM-FV2', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -29,7 +30,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM-FV2', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -40,7 +41,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM-FV2', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -51,7 +52,7 @@ def test_clw_fix():
 def test_get_fgco2_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM-FV2', 'Omon', 'fgco2')
-    assert fix == [Fgco2(None), Omon(None)]
+    assert fix == [Fgco2(None), Omon(None), GenericFix(None)]
 
 
 def test_fgco2_fix():
@@ -62,7 +63,7 @@ def test_fgco2_fix():
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM-FV2', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
@@ -73,7 +74,7 @@ def test_siconc_fix():
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2-WACCM-FV2', 'Amon', 'tas')
-    assert fix == [Tas(None)]
+    assert fix == [Tas(None), GenericFix(None)]
 
 
 def test_tas_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_ciesm.py b/tests/integration/cmor/_fixes/cmip6/test_ciesm.py
index 29da65c02..1cf1aa3aa 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_ciesm.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_ciesm.py
@@ -4,13 +4,14 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.ciesm import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CIESM', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 @pytest.fixture
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cmcc_cm2_sr5.py b/tests/integration/cmor/_fixes/cmip6/test_cmcc_cm2_sr5.py
index 44f410427..27aa30628 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cmcc_cm2_sr5.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cmcc_cm2_sr5.py
@@ -6,6 +6,7 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.cmcc_cm2_sr5 import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -13,7 +14,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CMCC-CM2-SR5', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 @pytest.fixture
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py
index 8322b1e7f..18ca8a3fa 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py
@@ -4,8 +4,14 @@ import iris
 import numpy as np
 import pytest
 
-from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import (Cl, Clcalipso,
-                                                     Cli, Clw, Omon)
+from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import (
+    Cl,
+    Clcalipso,
+    Cli,
+    Clw,
+    Omon,
+)
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -14,7 +20,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 AIR_PRESSURE_POINTS = np.array([[[[1.0, 1.0],
@@ -85,7 +91,7 @@ def test_cl_fix_metadata(test_data_path):
 def test_get_clcalipso_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'CFmon', 'clcalipso')
-    assert fix == [Clcalipso(None)]
+    assert fix == [Clcalipso(None), GenericFix(None)]
 
 
 @pytest.fixture
@@ -113,7 +119,7 @@ def test_clcalipso_fix_metadata(clcalipso_cubes):
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -124,7 +130,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -135,4 +141,4 @@ def test_clw_fix():
 def test_get_thetao_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'Omon', 'thetao')
-    assert fix == [Omon(None)]
+    assert fix == [Omon(None), GenericFix(None)]
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py
index fa43c06ac..9ac3e8bad 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py
@@ -3,13 +3,13 @@ from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cl as BaseCl
 from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cli as BaseCli
 from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Clw as BaseClw
 from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr import Cl, Cli, Clw
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1-HR', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -20,7 +20,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1-HR', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -31,7 +31,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1-HR', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py
index 8ff7b7e0a..3e6d66ebb 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py
@@ -7,16 +7,21 @@ from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cl as BaseCl
 from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Clcalipso as BaseClcalipso
 from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cli as BaseCli
 from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Clw as BaseClw
-from esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1 import (Cl, Clcalipso,
-                                                      Cli, Clw, Omon)
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1 import (
+    Cl,
+    Clcalipso,
+    Cli,
+    Clw,
+    Omon,
+)
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 from esmvalcore.cmor.table import get_var_info
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -27,7 +32,7 @@ def test_cl_fix():
 def test_get_clcalipso_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Amon', 'clcalipso')
-    assert fix == [Clcalipso(None)]
+    assert fix == [Clcalipso(None), GenericFix(None)]
 
 
 def test_clcalipso_fix():
@@ -38,7 +43,7 @@ def test_clcalipso_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -49,7 +54,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -88,7 +93,7 @@ def thetao_cubes():
 def test_get_thetao_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Omon', 'thetao')
-    assert fix == [Omon(None)]
+    assert fix == [Omon(None), GenericFix(None)]
 
 
 def test_thetao_fix_metadata(thetao_cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_e3sm_1_0.py b/tests/integration/cmor/_fixes/cmip6/test_e3sm_1_0.py
index e86de9e3a..890c81fd6 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_e3sm_1_0.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_e3sm_1_0.py
@@ -1,13 +1,14 @@
 """Tests for the fixes of E3SM-1-0."""
 from esmvalcore.cmor._fixes.cmip6.e3sm_1_0 import Cl
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'E3SM-1-0', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_ec_earth3_veg.py b/tests/integration/cmor/_fixes/cmip6/test_ec_earth3_veg.py
index f30904b46..b9145e6e8 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_ec_earth3_veg.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_ec_earth3_veg.py
@@ -14,6 +14,7 @@ from esmvalcore.cmor._fixes.cmip6.ec_earth3_veg import (
     Siconca,
     Tas,
 )
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -30,7 +31,7 @@ class TestSiconca(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('CMIP6', 'EC-Earth3-Veg', 'SImon', 'siconca'),
-            [Siconca(None)])
+            [Siconca(None), GenericFix(None)])
 
     def test_fix_data(self):
         """Test data fix."""
@@ -41,13 +42,13 @@ class TestSiconca(unittest.TestCase):
 
 def test_get_siconc_fix():
     """Test sinconc calendar is fixed."""
-    fix, = Fix.get_fixes('CMIP6', 'EC-Earth3-Veg', 'SImon', 'siconc')
+    fix = Fix.get_fixes('CMIP6', 'EC-Earth3-Veg', 'SImon', 'siconc')[0]
     assert isinstance(fix, CalendarFix)
 
 
 def test_get_tos_fix():
     """Test tos calendar is fixed."""
-    fix, = Fix.get_fixes('CMIP6', 'EC-Earth3-Veg', 'Omon', 'tos')
+    fix = Fix.get_fixes('CMIP6', 'EC-Earth3-Veg', 'Omon', 'tos')[0]
     assert isinstance(fix, CalendarFix)
 
 
@@ -110,7 +111,7 @@ def tas_cubes():
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'EC-Earth3-Veg', 'Amon', 'tas')
-    assert fix == [Tas(None)]
+    assert fix == [Tas(None), GenericFix(None)]
 
 
 def test_tas_fix_metadata(tas_cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_ec_earth3_veg_lr.py b/tests/integration/cmor/_fixes/cmip6/test_ec_earth3_veg_lr.py
index 22abcc7d3..b3f7963a7 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_ec_earth3_veg_lr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_ec_earth3_veg_lr.py
@@ -1,13 +1,13 @@
 """Test fixes for EC-Earth3-Veg-LR."""
 from esmvalcore.cmor._fixes.cmip6.ec_earth3_veg_lr import Siconc
 from esmvalcore.cmor._fixes.common import OceanFixGrid
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'EC-Earth3-Veg-LR', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_fgoals_f3_l.py b/tests/integration/cmor/_fixes/cmip6/test_fgoals_f3_l.py
index 450158ca7..7e9aa38d0 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_fgoals_f3_l.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_fgoals_f3_l.py
@@ -1,17 +1,13 @@
 """Tests for the fixes of FGOALS-f3-L."""
 
-import numpy as np
 import iris
+import numpy as np
 import pytest
 from cf_units import Unit
 
-from esmvalcore.cmor._fixes.cmip6.fgoals_f3_l import (
-    AllVars,
-    Clt,
-    Sftlf,
-    Tos,
-)
+from esmvalcore.cmor._fixes.cmip6.fgoals_f3_l import AllVars, Clt, Sftlf, Tos
 from esmvalcore.cmor._fixes.common import OceanFixGrid
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -80,7 +76,7 @@ def cubes():
 
 def test_get_allvars_fix():
     fix = Fix.get_fixes('CMIP6', 'FGOALS-f3-L', 'Amon', 'wrong_time_bnds')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_metadata(cubes):
@@ -104,7 +100,7 @@ def test_tos_fix():
 def test_get_clt_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'FGOALS-f3-l', 'Amon', 'clt')
-    assert fix == [Clt(None), AllVars(None)]
+    assert fix == [Clt(None), AllVars(None), GenericFix(None)]
 
 
 @pytest.fixture
@@ -129,7 +125,7 @@ def test_clt_fix_data(clt_cube):
 def test_get_sftlf_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'FGOALS-f3-l', 'Amon', 'sftlf')
-    assert fix == [Sftlf(None), AllVars(None)]
+    assert fix == [Sftlf(None), AllVars(None), GenericFix(None)]
 
 
 def test_sftlf_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_fgoals_g3.py b/tests/integration/cmor/_fixes/cmip6/test_fgoals_g3.py
index 630aa99fc..8bb945702 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_fgoals_g3.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_fgoals_g3.py
@@ -6,6 +6,7 @@ import numpy as np
 
 from esmvalcore.cmor._fixes.cmip6.fgoals_g3 import Mrsos, Siconc, Tos
 from esmvalcore.cmor._fixes.common import OceanFixGrid
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -13,7 +14,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_tos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Omon', 'tos')
-    assert fix == [Tos(None)]
+    assert fix == [Tos(None), GenericFix(None)]
 
 
 def test_tos_fix():
@@ -56,7 +57,7 @@ def test_tos_fix_metadata(mock_base_fix_metadata):
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
@@ -67,7 +68,7 @@ def test_siconc_fix():
 def test_get_mrsos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'FGOALS-g3', 'Lmon', 'mrsos')
-    assert fix == [Mrsos(None)]
+    assert fix == [Mrsos(None), GenericFix(None)]
 
 
 def test_mrsos_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_fio_esm_2_0.py b/tests/integration/cmor/_fixes/cmip6/test_fio_esm_2_0.py
index 36696f122..4ac1d05d4 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_fio_esm_2_0.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_fio_esm_2_0.py
@@ -6,6 +6,7 @@ from cf_units import Unit
 
 from esmvalcore.cmor._fixes.cmip6.fio_esm_2_0 import Amon, Omon, Tos
 from esmvalcore.cmor._fixes.common import OceanFixGrid
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -13,13 +14,13 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'FIO-ESM-2-0', 'Amon', 'tas')
-    assert fix == [Amon(None)]
+    assert fix == [Amon(None), GenericFix(None)]
 
 
 def test_get_tos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'FIO-ESM-2-0', 'Omon', 'tos')
-    assert fix == [OceanFixGrid(None), Omon(None)]
+    assert fix == [OceanFixGrid(None), Omon(None), GenericFix(None)]
 
 
 def test_tos_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py b/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py
index e52b55a00..f28310c6c 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py
@@ -3,9 +3,16 @@ import iris
 import numpy as np
 import pytest
 
-from esmvalcore.cmor._fixes.cmip6.gfdl_cm4 import (Cl, Cli, Clw,
-                                                   Fgco2, Omon, Siconc)
+from esmvalcore.cmor._fixes.cmip6.gfdl_cm4 import (
+    Cl,
+    Cli,
+    Clw,
+    Fgco2,
+    Omon,
+    Siconc,
+)
 from esmvalcore.cmor._fixes.common import SiconcFixScalarCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -13,7 +20,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GFDL-CM4', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 AIR_PRESSURE_POINTS = np.array([[[[1.0, 1.0],
@@ -75,7 +82,7 @@ def test_cl_fix_metadata(test_data_path):
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GFDL-CM4', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -86,7 +93,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GFDL-CM4', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -97,13 +104,13 @@ def test_clw_fix():
 def test_get_fgco2_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GFDL-CM4', 'Omon', 'fgco2')
-    assert fix == [Fgco2(None), Omon(None)]
+    assert fix == [Fgco2(None), Omon(None), GenericFix(None)]
 
 
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GFDL-CM4', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_gfdl_esm4.py b/tests/integration/cmor/_fixes/cmip6/test_gfdl_esm4.py
index d21323bb1..1db8eca4a 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_gfdl_esm4.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_gfdl_esm4.py
@@ -6,6 +6,7 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.gfdl_esm4 import Fgco2, Omon, Siconc
 from esmvalcore.cmor._fixes.common import SiconcFixScalarCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -13,7 +14,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GFDL-ESM4', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
@@ -52,7 +53,7 @@ def thetao_cubes():
 def test_get_thetao_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GFDL-ESM4', 'Omon', 'thetao')
-    assert fix == [Omon(None)]
+    assert fix == [Omon(None), GenericFix(None)]
 
 
 def test_thetao_fix_metadata(thetao_cubes):
@@ -76,7 +77,7 @@ def test_thetao_fix_metadata(thetao_cubes):
 def test_get_fgco2_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GFDL-ESM4', 'Omon', 'fgco2')
-    assert fix == [Fgco2(None), Omon(None)]
+    assert fix == [Fgco2(None), Omon(None), GenericFix(None)]
 
 
 def test_fgco2_fix_metadata():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py
index 9c88dc61a..40e61adb8 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py
@@ -5,13 +5,13 @@ from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip6.giss_e2_1_g import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-G', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -22,7 +22,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-G', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -33,7 +33,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-G', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -42,7 +42,7 @@ def test_clw_fix():
 
 
 def test_tos_fix():
-    fix, = Fix.get_fixes('CMIP6', 'GISS-E2-1-G', 'Omon', 'tos')
+    fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-G', 'Omon', 'tos')[0]
     cube = Cube(
         da.array([274], dtype=np.float32),
         var_name='tos',
diff --git a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py
index f8afbeea0..482e72cd2 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py
@@ -1,13 +1,13 @@
 """Test fixes for GISS-E2-1-H."""
 from esmvalcore.cmor._fixes.cmip6.giss_e2_1_h import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-H', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +18,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-H', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +29,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-H', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_hadgem3_gc31_ll.py b/tests/integration/cmor/_fixes/cmip6/test_hadgem3_gc31_ll.py
index a6543060b..aa9e7c34c 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_hadgem3_gc31_ll.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_hadgem3_gc31_ll.py
@@ -4,6 +4,7 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.hadgem3_gc31_ll import AllVars, Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridHeightCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -16,7 +17,7 @@ def sample_cubes():
 
 def test_get_tas_fix():
     fix = Fix.get_fixes('CMIP6', 'HadGEM3-GC31-LL', 'Amon', 'tas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_metadata(sample_cubes):
@@ -40,7 +41,7 @@ def test_allvars_no_need_tofix_metadata(sample_cubes):
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'HadGEM3-GC31-LL', 'Amon', 'cl')
-    assert fix == [Cl(None), AllVars(None)]
+    assert fix == [Cl(None), AllVars(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -51,7 +52,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'HadGEM3-GC31-LL', 'Amon', 'cli')
-    assert fix == [Cli(None), AllVars(None)]
+    assert fix == [Cli(None), AllVars(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -62,7 +63,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'HadGEM3-GC31-LL', 'Amon', 'clw')
-    assert fix == [Clw(None), AllVars(None)]
+    assert fix == [Clw(None), AllVars(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_icon_esm_lr.py b/tests/integration/cmor/_fixes/cmip6/test_icon_esm_lr.py
index 705a8bb5e..dfd183788 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_icon_esm_lr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_icon_esm_lr.py
@@ -4,6 +4,7 @@ from iris.coords import AuxCoord
 from iris.cube import Cube, CubeList
 
 from esmvalcore.cmor._fixes.cmip6.icon_esm_lr import AllVars
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -34,7 +35,7 @@ def cubes():
 def test_get_allvars_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'ICON-ESM-LR', 'Amon', 'tas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_metadata_lat_lon(cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_iitm_esm.py b/tests/integration/cmor/_fixes/cmip6/test_iitm_esm.py
index 52380e87f..16efd1d75 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_iitm_esm.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_iitm_esm.py
@@ -6,6 +6,7 @@ from cf_units import Unit
 
 from esmvalcore.cmor._fixes.cmip6.iitm_esm import AllVars, Tos
 from esmvalcore.cmor._fixes.common import OceanFixGrid
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -16,7 +17,7 @@ def test_get_tos_fix():
                         'Omon',
                         'tos',
                         extra_facets={"frequency": "mon"})
-    assert fix == [Tos(None), AllVars(None)]
+    assert fix == [Tos(None), AllVars(None), GenericFix(None)]
 
 
 def test_tos_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py b/tests/integration/cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py
index 99f2a76aa..c32008ef0 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py
@@ -9,7 +9,7 @@ from iris.cube import Cube, CubeList
 from iris.exceptions import CoordinateNotFoundError
 
 from esmvalcore.cmor._fixes.cmip6.ipsl_cm6a_lr import AllVars, Clcalipso, Omon
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 from esmvalcore.cmor.table import get_var_info
 
 
@@ -79,7 +79,7 @@ class TestAllVars(unittest.TestCase):
 def test_get_clcalipso_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'IPSL-CM6A-LR', 'CFmon', 'clcalipso')
-    assert fix == [Clcalipso(None), AllVars(None)]
+    assert fix == [Clcalipso(None), AllVars(None), GenericFix(None)]
 
 
 @pytest.fixture
@@ -137,7 +137,7 @@ def thetao_cubes():
 def test_get_thetao_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'IPSL-CM6A-LR', 'Omon', 'thetao')
-    assert fix == [Omon(None), AllVars(None)]
+    assert fix == [Omon(None), AllVars(None), GenericFix(None)]
 
 
 def test_thetao_fix_metadata(thetao_cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_kace_1_0_g.py b/tests/integration/cmor/_fixes/cmip6/test_kace_1_0_g.py
index 21e35f0c0..5d6b4641d 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_kace_1_0_g.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_kace_1_0_g.py
@@ -6,13 +6,14 @@ from cf_units import Unit
 
 from esmvalcore.cmor._fixes.cmip6.kace_1_0_g import AllVars, Cl, Cli, Clw, Tos
 from esmvalcore.cmor._fixes.common import ClFixHybridHeightCoord, OceanFixGrid
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'KACE-1-0-G', 'Amon', 'cl')
-    assert fix == [Cl(None), AllVars(None)]
+    assert fix == [Cl(None), AllVars(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -23,7 +24,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'KACE-1-0-G', 'Amon', 'cli')
-    assert fix == [Cli(None), AllVars(None)]
+    assert fix == [Cli(None), AllVars(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -34,7 +35,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'KACE-1-0-G', 'Amon', 'clw')
-    assert fix == [Clw(None), AllVars(None)]
+    assert fix == [Clw(None), AllVars(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -45,7 +46,7 @@ def test_clw_fix():
 def test_get_tos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'KACE-1-0-G', 'Omon', 'tos')
-    assert fix == [Tos(None), AllVars(None)]
+    assert fix == [Tos(None), AllVars(None), GenericFix(None)]
 
 
 def test_tos_fix():
@@ -101,7 +102,7 @@ def tos_cubes():
 
 def test_get_allvars_fix():
     fix = Fix.get_fixes('CMIP6', 'KACE-1-0-G', 'Omon', 'tos')
-    assert fix == [OceanFixGrid(None), AllVars(None)]
+    assert fix == [OceanFixGrid(None), AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_metadata(monkeypatch, tos_cubes, caplog):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_kiost_esm.py b/tests/integration/cmor/_fixes/cmip6/test_kiost_esm.py
index 9b460db15..80af6436e 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_kiost_esm.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_kiost_esm.py
@@ -8,7 +8,7 @@ from iris.cube import Cube, CubeList
 
 from esmvalcore.cmor._fixes.cmip6.kiost_esm import SfcWind, Siconc, Tas
 from esmvalcore.cmor._fixes.common import SiconcFixScalarCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 from esmvalcore.cmor.table import get_var_info
 
 
@@ -72,7 +72,7 @@ def tas_cubes():
 
 def test_get_sfcwind_fix():
     fix = Fix.get_fixes('CMIP6', 'KIOST-ESM', 'Amon', 'sfcWind')
-    assert fix == [SfcWind(None)]
+    assert fix == [SfcWind(None), GenericFix(None)]
 
 
 def test_sfcwind_fix_metadata(sfcwind_cubes):
@@ -104,7 +104,7 @@ def test_sfcwind_fix_metadata(sfcwind_cubes):
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'KIOST-ESM', 'SImon', 'siconc')
-    assert fix == [Siconc(None)]
+    assert fix == [Siconc(None), GenericFix(None)]
 
 
 def test_siconc_fix():
@@ -127,7 +127,7 @@ def test_siconc_fix_data():
 
 def test_get_tas_fix():
     fix = Fix.get_fixes('CMIP6', 'KIOST-ESM', 'Amon', 'tas')
-    assert fix == [Tas(None)]
+    assert fix == [Tas(None), GenericFix(None)]
 
 
 def test_tas_fix_metadata(tas_cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_mcm_ua_1_0.py b/tests/integration/cmor/_fixes/cmip6/test_mcm_ua_1_0.py
index bbb1a98f3..85eed2533 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_mcm_ua_1_0.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_mcm_ua_1_0.py
@@ -5,6 +5,7 @@ import pytest
 from cf_units import Unit
 
 from esmvalcore.cmor._fixes.cmip6.mcm_ua_1_0 import AllVars, Omon, Tas, Uas
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -119,17 +120,17 @@ def cubes_bounds():
 def test_get_allvars_fix():
     fix = Fix.get_fixes('CMIP6', 'MCM-UA-1-0', 'Amon',
                         'arbitrary_var_name_and_wrong_lon_bnds')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_get_tas_fix():
     fix = Fix.get_fixes('CMIP6', 'MCM-UA-1-0', 'Amon', 'tas')
-    assert fix == [Tas(None), AllVars(None)]
+    assert fix == [Tas(None), AllVars(None), GenericFix(None)]
 
 
 def test_get_uas_fix():
     fix = Fix.get_fixes('CMIP6', 'MCM-UA-1-0', 'Amon', 'uas')
-    assert fix == [Uas(None), AllVars(None)]
+    assert fix == [Uas(None), AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_metadata(cubes):
@@ -265,7 +266,7 @@ def thetao_cubes():
 def test_get_thetao_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MCM-UA-1-0', 'Omon', 'thetao')
-    assert fix == [Omon(None), AllVars(None)]
+    assert fix == [Omon(None), AllVars(None), GenericFix(None)]
 
 
 def test_thetao_fix_metadata(thetao_cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_miroc6.py b/tests/integration/cmor/_fixes/cmip6/test_miroc6.py
index 9c021d9ce..26662a0ec 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_miroc6.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_miroc6.py
@@ -5,14 +5,14 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.miroc6 import Cl, Cli, Clw, Tos
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 from esmvalcore.cmor.table import get_var_info
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MIROC6', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -23,7 +23,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MIROC6', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -34,7 +34,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MIROC6', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -69,7 +69,7 @@ def tos_cubes():
 def test_get_tos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MIROC6', 'Omon', 'tos')
-    assert fix == [Tos(None)]
+    assert fix == [Tos(None), GenericFix(None)]
 
 
 def test_tos_fix_metadata(tos_cubes):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py b/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py
index 550f01d80..22ae2d1c0 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py
@@ -1,13 +1,13 @@
 """Test fixes for MIROC-ES2L."""
 from esmvalcore.cmor._fixes.cmip6.miroc_es2l import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MIROC-ES2L', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +18,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MIROC-ES2L', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +29,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MIROC-ES2L', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_lr.py b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_lr.py
index 0f05c763c..2ae3af935 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_lr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_lr.py
@@ -1,13 +1,14 @@
 """Tests for the fixes of MPI-ESM1-2-LR."""
 from esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_lr import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MPI-ESM1-2-LR', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +19,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MPI-ESM1-2-LR', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +30,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MPI-ESM1-2-LR', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm_1_2_ham.py b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm_1_2_ham.py
index 492aa45f5..358cb1f56 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm_1_2_ham.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm_1_2_ham.py
@@ -1,13 +1,14 @@
 """Tests for the fixes of MPI-ESM-1-2-HAM."""
 from esmvalcore.cmor._fixes.cmip6.mpi_esm_1_2_ham import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MPI-ESM-1-2-HAM', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +19,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MPI-ESM-1-2-HAM', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +30,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MPI-ESM-1-2-HAM', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py b/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py
index 609cd12ab..6ec68855b 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py
@@ -1,13 +1,13 @@
 """Test fixes for MRI-ESM2-0."""
 from esmvalcore.cmor._fixes.cmip6.mri_esm2_0 import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MRI-ESM2-0', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +18,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MRI-ESM2-0', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +29,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'MRI-ESM2-0', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_nesm3.py b/tests/integration/cmor/_fixes/cmip6/test_nesm3.py
index c1e1fe5f9..bc9265bc5 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_nesm3.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_nesm3.py
@@ -1,13 +1,13 @@
 """Test fixes for NESM3."""
 from esmvalcore.cmor._fixes.cmip6.nesm3 import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NESM3', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +18,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NESM3', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +29,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NESM3', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_noresm2_lm.py b/tests/integration/cmor/_fixes/cmip6/test_noresm2_lm.py
index c77e945de..78ea66eff 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_noresm2_lm.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_noresm2_lm.py
@@ -12,6 +12,7 @@ from esmvalcore.cmor._fixes.cmip6.noresm2_lm import (
     Siconc,
 )
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -19,7 +20,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NorESM2-LM', 'Amon', 'cl')
-    assert fix == [Cl(None), AllVars(None)]
+    assert fix == [Cl(None), AllVars(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -30,7 +31,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NorESM2-LM', 'Amon', 'cli')
-    assert fix == [Cli(None), AllVars(None)]
+    assert fix == [Cli(None), AllVars(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -41,7 +42,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NorESM2-LM', 'Amon', 'clw')
-    assert fix == [Clw(None), AllVars(None)]
+    assert fix == [Clw(None), AllVars(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -97,7 +98,7 @@ def cubes_bounds():
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NorESM2-LM', 'SImon', 'siconc')
-    assert fix == [Siconc(None), AllVars(None)]
+    assert fix == [Siconc(None), AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_lon_bounds(cubes_bounds):
diff --git a/tests/integration/cmor/_fixes/cmip6/test_noresm2_mm.py b/tests/integration/cmor/_fixes/cmip6/test_noresm2_mm.py
index 2fd3a9c7f..0868dd910 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_noresm2_mm.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_noresm2_mm.py
@@ -1,13 +1,14 @@
 """Tests for the fixes of NorESM2-MM."""
 from esmvalcore.cmor._fixes.cmip6.noresm2_mm import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NorESM2-MM', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +19,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NorESM2-MM', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +30,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'NorESM2-MM', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py b/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py
index 09d35be72..e9f0a76a5 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py
@@ -5,13 +5,13 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.sam0_unicon import Cl, Cli, Clw, Nbp
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
-from esmvalcore.cmor._fixes.fix import Fix
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'SAM0-UNICON', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -22,7 +22,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'SAM0-UNICON', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -33,7 +33,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'SAM0-UNICON', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
@@ -44,7 +44,7 @@ def test_clw_fix():
 def test_get_nbp_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'SAM0-UNICON', 'Lmon', 'nbp')
-    assert fix == [Nbp(None)]
+    assert fix == [Nbp(None), GenericFix(None)]
 
 
 @pytest.fixture
diff --git a/tests/integration/cmor/_fixes/cmip6/test_taiesm1.py b/tests/integration/cmor/_fixes/cmip6/test_taiesm1.py
index 107db534b..dd955ef1f 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_taiesm1.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_taiesm1.py
@@ -1,13 +1,14 @@
 """Tests for the fixes of TaiESM1."""
 from esmvalcore.cmor._fixes.cmip6.taiesm1 import Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'TaiESM1', 'Amon', 'cl')
-    assert fix == [Cl(None)]
+    assert fix == [Cl(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -18,7 +19,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'TaiESM1', 'Amon', 'cli')
-    assert fix == [Cli(None)]
+    assert fix == [Cli(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -29,7 +30,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'TaiESM1', 'Amon', 'clw')
-    assert fix == [Clw(None)]
+    assert fix == [Clw(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/cmip6/test_ukesm1_0_ll.py b/tests/integration/cmor/_fixes/cmip6/test_ukesm1_0_ll.py
index f6aceb121..423d55e63 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_ukesm1_0_ll.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_ukesm1_0_ll.py
@@ -4,6 +4,7 @@ import pytest
 
 from esmvalcore.cmor._fixes.cmip6.ukesm1_0_ll import AllVars, Cl, Cli, Clw
 from esmvalcore.cmor._fixes.common import ClFixHybridHeightCoord
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 
 
@@ -18,7 +19,7 @@ def sample_cubes():
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'UKESM1-0-LL', 'Amon', 'tas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_metadata(sample_cubes):
@@ -44,7 +45,7 @@ def test_allvars_no_need_tofix_metadata(sample_cubes):
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'UKESM1-0-LL', 'Amon', 'cl')
-    assert fix == [Cl(None), AllVars(None)]
+    assert fix == [Cl(None), AllVars(None), GenericFix(None)]
 
 
 def test_cl_fix():
@@ -55,7 +56,7 @@ def test_cl_fix():
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'UKESM1-0-LL', 'Amon', 'cli')
-    assert fix == [Cli(None), AllVars(None)]
+    assert fix == [Cli(None), AllVars(None), GenericFix(None)]
 
 
 def test_cli_fix():
@@ -66,7 +67,7 @@ def test_cli_fix():
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'UKESM1-0-LL', 'Amon', 'clw')
-    assert fix == [Clw(None), AllVars(None)]
+    assert fix == [Clw(None), AllVars(None), GenericFix(None)]
 
 
 def test_clw_fix():
diff --git a/tests/integration/cmor/_fixes/emac/test_emac.py b/tests/integration/cmor/_fixes/emac/test_emac.py
index 15c5b76a6..1146502a4 100644
--- a/tests/integration/cmor/_fixes/emac/test_emac.py
+++ b/tests/integration/cmor/_fixes/emac/test_emac.py
@@ -36,6 +36,7 @@ from esmvalcore.cmor._fixes.emac.emac import (
     Toz,
     Zg,
 )
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import CoordinateInfo, get_var_info
 from esmvalcore.config._config import get_extra_facets
@@ -834,7 +835,7 @@ def test_sample_data_ta_alevel(test_data_path, tmp_path):
 def test_get_awhea_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Omon', 'awhea')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_awhea_fix(cubes_2d):
@@ -859,7 +860,7 @@ def test_awhea_fix(cubes_2d):
 def test_get_clivi_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'clivi')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_clivi_fix(cubes_2d):
@@ -883,7 +884,7 @@ def test_clivi_fix(cubes_2d):
 def test_get_clt_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'clt')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_clt_fix(cubes_2d):
@@ -906,7 +907,7 @@ def test_clt_fix(cubes_2d):
 def test_get_clwvi_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'clwvi')
-    assert fix == [Clwvi(None), AllVars(None)]
+    assert fix == [Clwvi(None), AllVars(None), GenericFix(None)]
 
 
 def test_clwvi_fix(cubes_2d):
@@ -933,7 +934,7 @@ def test_clwvi_fix(cubes_2d):
 def test_get_co2mass_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'co2mass')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_co2mass_fix(cubes_1d):
@@ -957,7 +958,7 @@ def test_co2mass_fix(cubes_1d):
 def test_get_evspsbl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'evspsbl')
-    assert fix == [Evspsbl(None), AllVars(None)]
+    assert fix == [Evspsbl(None), AllVars(None), GenericFix(None)]
 
 
 def test_evspsbl_fix(cubes_2d):
@@ -986,7 +987,7 @@ def test_evspsbl_fix(cubes_2d):
 def test_get_hfls_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'hfls')
-    assert fix == [Hfls(None), AllVars(None)]
+    assert fix == [Hfls(None), AllVars(None), GenericFix(None)]
 
 
 def test_hfls_fix(cubes_2d):
@@ -1014,7 +1015,7 @@ def test_hfls_fix(cubes_2d):
 def test_get_hfss_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'hfss')
-    assert fix == [Hfss(None), AllVars(None)]
+    assert fix == [Hfss(None), AllVars(None), GenericFix(None)]
 
 
 def test_hfss_fix(cubes_2d):
@@ -1042,7 +1043,7 @@ def test_hfss_fix(cubes_2d):
 def test_get_hurs_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'hurs')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_hurs_fix(cubes_2d):
@@ -1065,7 +1066,7 @@ def test_hurs_fix(cubes_2d):
 def test_get_od550aer_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'od550aer')
-    assert fix == [Od550aer(None), AllVars(None)]
+    assert fix == [Od550aer(None), AllVars(None), GenericFix(None)]
 
 
 def test_od550aer_fix(cubes_3d):
@@ -1090,7 +1091,7 @@ def test_od550aer_fix(cubes_3d):
 def test_get_pr_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'pr')
-    assert fix == [Pr(None), AllVars(None)]
+    assert fix == [Pr(None), AllVars(None), GenericFix(None)]
 
 
 def test_pr_fix(cubes_2d):
@@ -1116,7 +1117,7 @@ def test_pr_fix(cubes_2d):
 def test_get_prc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'prc')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_prc_fix(cubes_2d):
@@ -1140,7 +1141,7 @@ def test_prc_fix(cubes_2d):
 def test_get_prl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'prl')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_prl_fix(cubes_2d):
@@ -1164,7 +1165,7 @@ def test_prl_fix(cubes_2d):
 def test_get_prsn_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'prsn')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_prsn_fix(cubes_2d):
@@ -1188,7 +1189,7 @@ def test_prsn_fix(cubes_2d):
 def test_get_prw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'prw')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_prw_fix(cubes_2d):
@@ -1212,7 +1213,7 @@ def test_prw_fix(cubes_2d):
 def test_get_ps_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'ps')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_ps_fix(cubes_2d):
@@ -1236,7 +1237,7 @@ def test_ps_fix(cubes_2d):
 def test_get_psl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'psl')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_psl_fix(cubes_2d):
@@ -1260,7 +1261,7 @@ def test_psl_fix(cubes_2d):
 def test_get_rlds_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rlds')
-    assert fix == [Rlds(None), AllVars(None)]
+    assert fix == [Rlds(None), AllVars(None), GenericFix(None)]
 
 
 def test_rlds_fix(cubes_2d):
@@ -1285,7 +1286,7 @@ def test_rlds_fix(cubes_2d):
 def test_get_rlus_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rlus')
-    assert fix == [Rlus(None), AllVars(None)]
+    assert fix == [Rlus(None), AllVars(None), GenericFix(None)]
 
 
 def test_rlus_fix(cubes_2d):
@@ -1313,7 +1314,7 @@ def test_rlus_fix(cubes_2d):
 def test_get_rlut_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rlut')
-    assert fix == [Rlut(None), AllVars(None)]
+    assert fix == [Rlut(None), AllVars(None), GenericFix(None)]
 
 
 def test_rlut_fix(cubes_2d):
@@ -1341,7 +1342,7 @@ def test_rlut_fix(cubes_2d):
 def test_get_rlutcs_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rlutcs')
-    assert fix == [Rlutcs(None), AllVars(None)]
+    assert fix == [Rlutcs(None), AllVars(None), GenericFix(None)]
 
 
 def test_rlutcs_fix(cubes_2d):
@@ -1370,7 +1371,7 @@ def test_rlutcs_fix(cubes_2d):
 def test_get_rsds_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rsds')
-    assert fix == [Rsds(None), AllVars(None)]
+    assert fix == [Rsds(None), AllVars(None), GenericFix(None)]
 
 
 def test_rsds_fix(cubes_2d):
@@ -1395,7 +1396,7 @@ def test_rsds_fix(cubes_2d):
 def test_get_rsdt_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rsdt')
-    assert fix == [Rsdt(None), AllVars(None)]
+    assert fix == [Rsdt(None), AllVars(None), GenericFix(None)]
 
 
 def test_rsdt_fix(cubes_2d):
@@ -1420,7 +1421,7 @@ def test_rsdt_fix(cubes_2d):
 def test_get_rsus_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rsus')
-    assert fix == [Rsus(None), AllVars(None)]
+    assert fix == [Rsus(None), AllVars(None), GenericFix(None)]
 
 
 def test_rsus_fix(cubes_2d):
@@ -1448,7 +1449,7 @@ def test_rsus_fix(cubes_2d):
 def test_get_rsut_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rsut')
-    assert fix == [Rsut(None), AllVars(None)]
+    assert fix == [Rsut(None), AllVars(None), GenericFix(None)]
 
 
 def test_rsut_fix(cubes_2d):
@@ -1476,7 +1477,7 @@ def test_rsut_fix(cubes_2d):
 def test_get_rsutcs_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rsutcs')
-    assert fix == [Rsutcs(None), AllVars(None)]
+    assert fix == [Rsutcs(None), AllVars(None), GenericFix(None)]
 
 
 def test_rsutcs_fix(cubes_2d):
@@ -1505,7 +1506,7 @@ def test_rsutcs_fix(cubes_2d):
 def test_get_rtmt_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'rtmt')
-    assert fix == [Rtmt(None), AllVars(None)]
+    assert fix == [Rtmt(None), AllVars(None), GenericFix(None)]
 
 
 def test_rtmt_fix(cubes_2d):
@@ -1531,7 +1532,7 @@ def test_rtmt_fix(cubes_2d):
 def test_get_sfcWind_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'sfcWind')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_sfcWind_fix(cubes_2d):  # noqa: N802
@@ -1557,7 +1558,7 @@ def test_sfcWind_fix(cubes_2d):  # noqa: N802
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'SImon', 'siconc')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_siconc_fix(cubes_2d):
@@ -1582,7 +1583,7 @@ def test_siconc_fix(cubes_2d):
 def test_get_siconca_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'SImon', 'siconca')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_siconca_fix(cubes_2d):
@@ -1607,7 +1608,7 @@ def test_siconca_fix(cubes_2d):
 def test_get_sithick_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'SImon', 'sithick')
-    assert fix == [Sithick(None), AllVars(None)]
+    assert fix == [Sithick(None), AllVars(None), GenericFix(None)]
 
 
 def test_sithick_fix(cubes_2d):
@@ -1642,7 +1643,7 @@ def test_sithick_fix(cubes_2d):
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'tas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_tas_fix(cubes_2d):
@@ -1668,7 +1669,7 @@ def test_tas_fix(cubes_2d):
 def test_get_tasmax_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'tasmax')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_tasmax_fix(cubes_2d):
@@ -1694,7 +1695,7 @@ def test_tasmax_fix(cubes_2d):
 def test_get_tasmin_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'tasmin')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_tasmin_fix(cubes_2d):
@@ -1720,7 +1721,7 @@ def test_tasmin_fix(cubes_2d):
 def test_get_tauu_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'tauu')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_tauu_fix(cubes_2d):
@@ -1744,7 +1745,7 @@ def test_tauu_fix(cubes_2d):
 def test_get_tauv_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'tauv')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_tauv_fix(cubes_2d):
@@ -1768,7 +1769,7 @@ def test_tauv_fix(cubes_2d):
 def test_get_tos_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Omon', 'tos')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_tos_fix(cubes_2d):
@@ -1792,7 +1793,7 @@ def test_tos_fix(cubes_2d):
 def test_get_toz_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'AERmon', 'toz')
-    assert fix == [Toz(None), AllVars(None)]
+    assert fix == [Toz(None), AllVars(None), GenericFix(None)]
 
 
 def test_toz_fix(cubes_2d):
@@ -1816,7 +1817,7 @@ def test_toz_fix(cubes_2d):
 def test_get_ts_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'ts')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_ts_fix(cubes_2d):
@@ -1840,7 +1841,7 @@ def test_ts_fix(cubes_2d):
 def test_get_uas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'uas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_uas_fix(cubes_2d):
@@ -1866,7 +1867,7 @@ def test_uas_fix(cubes_2d):
 def test_get_vas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'vas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_vas_fix(cubes_2d):
@@ -1895,7 +1896,7 @@ def test_vas_fix(cubes_2d):
 def test_get_MP_BC_tot_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_BC_tot')
-    assert fix == [MP_BC_tot(None), AllVars(None)]
+    assert fix == [MP_BC_tot(None), AllVars(None), GenericFix(None)]
 
 
 def test_MP_BC_tot_fix(cubes_1d):  # noqa: N802
@@ -1925,7 +1926,7 @@ def test_MP_BC_tot_fix(cubes_1d):  # noqa: N802
 def test_get_MP_CFCl3_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_CFCl3')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_CFCl3_fix(cubes_1d):  # noqa: N802
@@ -1949,7 +1950,7 @@ def test_MP_CFCl3_fix(cubes_1d):  # noqa: N802
 def test_get_MP_ClOX_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_ClOX')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_ClOX_fix(cubes_1d):  # noqa: N802
@@ -1973,7 +1974,7 @@ def test_MP_ClOX_fix(cubes_1d):  # noqa: N802
 def test_get_MP_CH4_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_CH4')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_CH4_fix(cubes_1d):  # noqa: N802
@@ -1997,7 +1998,7 @@ def test_MP_CH4_fix(cubes_1d):  # noqa: N802
 def test_get_MP_CO_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_CO')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_CO_fix(cubes_1d):  # noqa: N802
@@ -2021,7 +2022,7 @@ def test_MP_CO_fix(cubes_1d):  # noqa: N802
 def test_get_MP_CO2_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_CO2')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_CO2_fix(cubes_1d):  # noqa: N802
@@ -2045,7 +2046,7 @@ def test_MP_CO2_fix(cubes_1d):  # noqa: N802
 def test_get_MP_DU_tot_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_DU_tot')
-    assert fix == [MP_DU_tot(None), AllVars(None)]
+    assert fix == [MP_DU_tot(None), AllVars(None), GenericFix(None)]
 
 
 def test_MP_DU_tot_fix(cubes_1d):  # noqa: N802
@@ -2076,7 +2077,7 @@ def test_MP_DU_tot_fix(cubes_1d):  # noqa: N802
 def test_get_MP_N2O_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_N2O')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_N2O_fix(cubes_1d):  # noqa: N802
@@ -2100,7 +2101,7 @@ def test_MP_N2O_fix(cubes_1d):  # noqa: N802
 def test_get_MP_NH3_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_NH3')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_NH3_fix(cubes_1d):  # noqa: N802
@@ -2124,7 +2125,7 @@ def test_MP_NH3_fix(cubes_1d):  # noqa: N802
 def test_get_MP_NO_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_NO')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_NO_fix(cubes_1d):  # noqa: N802
@@ -2148,7 +2149,7 @@ def test_MP_NO_fix(cubes_1d):  # noqa: N802
 def test_get_MP_NO2_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_NO2')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_NO2_fix(cubes_1d):  # noqa: N802
@@ -2172,7 +2173,7 @@ def test_MP_NO2_fix(cubes_1d):  # noqa: N802
 def test_get_MP_NOX_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_NOX')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_NOX_fix(cubes_1d):  # noqa: N802
@@ -2196,7 +2197,7 @@ def test_MP_NOX_fix(cubes_1d):  # noqa: N802
 def test_get_MP_O3_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_O3')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_O3_fix(cubes_1d):  # noqa: N802
@@ -2220,7 +2221,7 @@ def test_MP_O3_fix(cubes_1d):  # noqa: N802
 def test_get_MP_OH_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_OH')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_OH_fix(cubes_1d):  # noqa: N802
@@ -2244,7 +2245,7 @@ def test_MP_OH_fix(cubes_1d):  # noqa: N802
 def test_get_MP_S_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_S')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_S_fix(cubes_1d):  # noqa: N802
@@ -2268,7 +2269,7 @@ def test_MP_S_fix(cubes_1d):  # noqa: N802
 def test_get_MP_SO2_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_SO2')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_MP_SO2_fix(cubes_1d):  # noqa: N802
@@ -2292,7 +2293,7 @@ def test_MP_SO2_fix(cubes_1d):  # noqa: N802
 def test_get_MP_SO4mm_tot_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_SO4mm_tot')
-    assert fix == [MP_SO4mm_tot(None), AllVars(None)]
+    assert fix == [MP_SO4mm_tot(None), AllVars(None), GenericFix(None)]
 
 
 def test_MP_SO4mm_tot_fix(cubes_1d):  # noqa: N802
@@ -2323,7 +2324,7 @@ def test_MP_SO4mm_tot_fix(cubes_1d):  # noqa: N802
 def test_get_MP_SS_tot_fix():  # noqa: N802
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'TRAC10hr', 'MP_SS_tot')
-    assert fix == [MP_SS_tot(None), AllVars(None)]
+    assert fix == [MP_SS_tot(None), AllVars(None), GenericFix(None)]
 
 
 def test_MP_SS_tot_fix(cubes_1d):  # noqa: N802
@@ -2355,7 +2356,7 @@ def test_MP_SS_tot_fix(cubes_1d):  # noqa: N802
 def test_get_cl_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'cl')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_cl_fix(cubes_3d):
@@ -2380,7 +2381,7 @@ def test_cl_fix(cubes_3d):
 def test_get_cli_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'cli')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_cli_fix(cubes_3d):
@@ -2406,7 +2407,7 @@ def test_cli_fix(cubes_3d):
 def test_get_clw_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'clw')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_clw_fix(cubes_3d):
@@ -2432,7 +2433,7 @@ def test_clw_fix(cubes_3d):
 def test_get_hur_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'hur')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_hur_fix(cubes_3d):
@@ -2459,7 +2460,7 @@ def test_hur_fix(cubes_3d):
 def test_get_hus_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'hus')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_hus_fix(cubes_3d):
@@ -2486,7 +2487,7 @@ def test_hus_fix(cubes_3d):
 def test_get_ta_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'ta')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_ta_fix(cubes_3d):
@@ -2513,7 +2514,7 @@ def test_ta_fix(cubes_3d):
 def test_get_ua_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'ua')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_ua_fix(cubes_3d):
@@ -2540,7 +2541,7 @@ def test_ua_fix(cubes_3d):
 def test_get_va_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'va')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_va_fix(cubes_3d):
@@ -2567,7 +2568,7 @@ def test_va_fix(cubes_3d):
 def test_get_zg_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('EMAC', 'EMAC', 'Amon', 'zg')
-    assert fix == [Zg(None), AllVars(None)]
+    assert fix == [Zg(None), AllVars(None), GenericFix(None)]
 
 
 def test_zg_fix(cubes_3d):
diff --git a/tests/integration/cmor/_fixes/icon/test_icon.py b/tests/integration/cmor/_fixes/icon/test_icon.py
index 297058a9a..be20131e5 100644
--- a/tests/integration/cmor/_fixes/icon/test_icon.py
+++ b/tests/integration/cmor/_fixes/icon/test_icon.py
@@ -12,6 +12,7 @@ from iris.coords import AuxCoord, CellMethod, DimCoord
 from iris.cube import Cube, CubeList
 
 import esmvalcore.cmor._fixes.icon.icon
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor._fixes.icon._base_fixes import IconFix
 from esmvalcore.cmor._fixes.icon.icon import AllVars, Clwvi
 from esmvalcore.cmor.fix import Fix
@@ -491,7 +492,7 @@ def check_typesi(cube):
 def test_get_areacella_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'fx', 'areacella')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_areacella_fix(cubes_grid):
@@ -513,7 +514,7 @@ def test_areacella_fix(cubes_grid):
 def test_get_areacello_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'Ofx', 'areacello')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_areacello_fix(cubes_grid):
@@ -538,7 +539,7 @@ def test_areacello_fix(cubes_grid):
 def test_get_clwvi_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'Amon', 'clwvi')
-    assert fix == [Clwvi(None), AllVars(None)]
+    assert fix == [Clwvi(None), AllVars(None), GenericFix(None)]
 
 
 def test_clwvi_fix(cubes_regular_grid):
@@ -572,7 +573,7 @@ def test_clwvi_fix(cubes_regular_grid):
 def test_get_lwp_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'AERmon', 'lwp')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_lwp_fix(cubes_2d):
@@ -599,7 +600,7 @@ def test_lwp_fix(cubes_2d):
 def test_get_rsdt_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'Amon', 'rsdt')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_rsdt_fix(cubes_2d):
@@ -622,7 +623,7 @@ def test_rsdt_fix(cubes_2d):
 def test_get_rsut_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'Amon', 'rsut')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_rsut_fix(cubes_2d):
@@ -648,7 +649,7 @@ def test_rsut_fix(cubes_2d):
 def test_get_siconc_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'SImon', 'siconc')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_siconc_fix(cubes_2d):
@@ -671,7 +672,7 @@ def test_siconc_fix(cubes_2d):
 def test_get_siconca_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'SImon', 'siconca')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_siconca_fix(cubes_2d):
@@ -697,7 +698,7 @@ def test_siconca_fix(cubes_2d):
 def test_get_ta_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'Amon', 'ta')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_ta_fix(cubes_3d):
@@ -732,7 +733,7 @@ def test_ta_fix_no_plev_bounds(cubes_3d):
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'Amon', 'tas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_tas_fix(cubes_2d):
@@ -854,7 +855,7 @@ def test_tas_no_shift_time(cubes_2d):
 def test_get_uas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'Amon', 'uas')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_uas_fix(cubes_2d):
@@ -951,7 +952,7 @@ def test_2d_lat_lon_grid_fix(cubes_2d_lat_lon_grid):
 def test_get_ch4clim_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('ICON', 'ICON', 'Amon', 'ch4Clim')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_ch4clim_fix(cubes_regular_grid):
diff --git a/tests/integration/cmor/_fixes/ipslcm/test_ipsl_cm6.py b/tests/integration/cmor/_fixes/ipslcm/test_ipsl_cm6.py
index f136114bc..dd1d19480 100644
--- a/tests/integration/cmor/_fixes/ipslcm/test_ipsl_cm6.py
+++ b/tests/integration/cmor/_fixes/ipslcm/test_ipsl_cm6.py
@@ -2,6 +2,7 @@
 import iris
 import pytest
 
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor._fixes.ipslcm.ipsl_cm6 import Tas
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
@@ -11,7 +12,7 @@ from esmvalcore.cmor.table import get_var_info
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('IPSLCM', 'IPSL-CM6', 'Amon', 'tas')
-    assert fix == [Tas(None)]
+    assert fix == [Tas(None), GenericFix(None)]
 
 
 @pytest.fixture
diff --git a/tests/integration/cmor/_fixes/native6/test_era5.py b/tests/integration/cmor/_fixes/native6/test_era5.py
index cfe20e7fd..6b8a4922b 100644
--- a/tests/integration/cmor/_fixes/native6/test_era5.py
+++ b/tests/integration/cmor/_fixes/native6/test_era5.py
@@ -6,14 +6,16 @@ import numpy as np
 import pytest
 from cf_units import Unit
 
+from esmvalcore.cmor._fixes.fix import Fix, GenericFix
 from esmvalcore.cmor._fixes.native6.era5 import (
     AllVars,
     Evspsbl,
     Zg,
     get_frequency,
 )
-from esmvalcore.cmor.fix import Fix, fix_metadata
-from esmvalcore.cmor.table import CMOR_TABLES
+from esmvalcore.cmor.fix import fix_metadata
+from esmvalcore.cmor.table import CMOR_TABLES, get_var_info
+from esmvalcore.preprocessor import cmor_check_metadata
 
 COMMENT = ('Contains modified Copernicus Climate Change Service Information '
            f'{datetime.datetime.now().year}')
@@ -22,13 +24,15 @@ COMMENT = ('Contains modified Copernicus Climate Change Service Information '
 def test_get_evspsbl_fix():
     """Test whether the right fixes are gathered for a single variable."""
     fix = Fix.get_fixes('native6', 'ERA5', 'E1hr', 'evspsbl')
-    assert fix == [Evspsbl(None), AllVars(None)]
+    vardef = get_var_info('native6', 'E1hr', 'evspsbl')
+    assert fix == [Evspsbl(vardef), AllVars(vardef), GenericFix(vardef)]
 
 
 def test_get_zg_fix():
     """Test whether the right fix gets found again, for zg as well."""
     fix = Fix.get_fixes('native6', 'ERA5', 'Amon', 'zg')
-    assert fix == [Zg(None), AllVars(None)]
+    vardef = get_var_info('native6', 'E1hr', 'evspsbl')
+    assert fix == [Zg(vardef), AllVars(vardef), GenericFix(vardef)]
 
 
 def test_get_frequency_hourly():
@@ -1027,9 +1031,14 @@ VARIABLES = [
 def test_cmorization(era5_cubes, cmor_cubes, var, mip):
     """Verify that cmorization results in the expected target cube."""
     fixed_cubes = fix_metadata(era5_cubes, var, 'native6', 'era5', mip)
+
     assert len(fixed_cubes) == 1
     fixed_cube = fixed_cubes[0]
     cmor_cube = cmor_cubes[0]
+
+    # Test that CMOR checks are passing
+    fixed_cubes = cmor_check_metadata(fixed_cube, 'native6', mip, var)
+
     if fixed_cube.coords('time'):
         for cube in [fixed_cube, cmor_cube]:
             coord = cube.coord('time')
diff --git a/tests/integration/cmor/_fixes/obs4mips/test_airs_2_1.py b/tests/integration/cmor/_fixes/obs4mips/test_airs_2_1.py
index 4ac2ce7f2..80ce0d00c 100644
--- a/tests/integration/cmor/_fixes/obs4mips/test_airs_2_1.py
+++ b/tests/integration/cmor/_fixes/obs4mips/test_airs_2_1.py
@@ -3,6 +3,7 @@ import numpy as np
 from iris.coords import DimCoord
 from iris.cube import Cube, CubeList
 
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor._fixes.obs4mips.airs_2_1 import AllVars
 from esmvalcore.cmor.fix import Fix
 
@@ -16,7 +17,7 @@ def get_air_pressure_coord(points, units):
 def test_get_allvars_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('obs4MIPs', 'AIRS-2-1', 'Amon', 'cl')
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_allvars_fix_no_air_pressure():
diff --git a/tests/integration/cmor/_fixes/obs4mips/test_ssmi.py b/tests/integration/cmor/_fixes/obs4mips/test_ssmi.py
index bde17735c..6a6648677 100644
--- a/tests/integration/cmor/_fixes/obs4mips/test_ssmi.py
+++ b/tests/integration/cmor/_fixes/obs4mips/test_ssmi.py
@@ -1,6 +1,7 @@
 """Test SSMI fixes."""
 import unittest
 
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor._fixes.obs4mips.ssmi import Prw
 from esmvalcore.cmor.fix import Fix
 
@@ -10,4 +11,4 @@ class TestPrw(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(Fix.get_fixes('obs4MIPs', 'SSMI', 'Amon', 'prw'),
-                             [Prw(None)])
+                             [Prw(None), GenericFix(None)])
diff --git a/tests/integration/cmor/_fixes/obs4mips/test_ssmi_meris.py b/tests/integration/cmor/_fixes/obs4mips/test_ssmi_meris.py
index 47ec98b12..024aa4e70 100644
--- a/tests/integration/cmor/_fixes/obs4mips/test_ssmi_meris.py
+++ b/tests/integration/cmor/_fixes/obs4mips/test_ssmi_meris.py
@@ -1,6 +1,7 @@
 """Test SSMI fixes."""
 import unittest
 
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor._fixes.obs4mips.ssmi_meris import Prw
 from esmvalcore.cmor.fix import Fix
 
@@ -11,4 +12,4 @@ class TestPrw(unittest.TestCase):
         """Test fix get."""
         self.assertListEqual(
             Fix.get_fixes('obs4MIPs', 'SSMI-MERIS', 'Amon', 'prw'),
-            [Prw(None)])
+            [Prw(None), GenericFix(None)])
diff --git a/tests/integration/cmor/_fixes/test_fix.py b/tests/integration/cmor/_fixes/test_fix.py
index 7abc73418..ea4ecec14 100644
--- a/tests/integration/cmor/_fixes/test_fix.py
+++ b/tests/integration/cmor/_fixes/test_fix.py
@@ -14,16 +14,22 @@ from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.cnrm_aladin63 import (
     Tas,
 )
 from esmvalcore.cmor._fixes.cordex.cordex_fixes import AllVars
+from esmvalcore.cmor._fixes.fix import GenericFix
 from esmvalcore.cmor.fix import Fix
+from esmvalcore.cmor.table import get_var_info
 from esmvalcore.config import CFG
 
 
 def test_get_fix():
-    assert Fix.get_fixes('CMIP5', 'CanESM2', 'Amon', 'fgco2') == [FgCo2(None)]
+    assert Fix.get_fixes('CMIP5', 'CanESM2', 'Amon', 'fgco2') == [
+        FgCo2(None), GenericFix(None)
+    ]
 
 
 def test_get_fix_case_insensitive():
-    assert Fix.get_fixes('CMIP5', 'CanESM2', 'Amon', 'fgCo2'), [FgCo2(None)]
+    assert Fix.get_fixes('CMIP5', 'CanESM2', 'Amon', 'fgCo2') == [
+        FgCo2(None), GenericFix(None)
+    ]
 
 
 def test_get_fix_cordex():
@@ -34,7 +40,7 @@ def test_get_fix_cordex():
         'tas',
         extra_facets={'driver': 'CNRM-CERFACS-CNRM-CM5'},
     )
-    assert fix == [Tas(None), AllVars(None)]
+    assert fix == [Tas(None), AllVars(None), GenericFix(None)]
 
 
 def test_get_grid_fix_cordex():
@@ -45,15 +51,19 @@ def test_get_grid_fix_cordex():
         'tas',
         extra_facets={'driver': 'CNRM-CERFACS-CNRM-CM5'},
     )
-    assert fix == [AllVars(None)]
+    assert fix == [AllVars(None), GenericFix(None)]
 
 
 def test_get_fixes_with_replace():
-    assert Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'ch4') == [Ch4(None)]
+    assert Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'ch4') == [
+        Ch4(None), GenericFix(None)
+    ]
 
 
 def test_get_fixes_with_generic():
-    assert Fix.get_fixes('CMIP5', 'CESM1-BGC', 'Amon', 'gpp') == [Gpp(None)]
+    assert Fix.get_fixes('CMIP5', 'CESM1-BGC', 'Amon', 'gpp') == [
+        Gpp(None), GenericFix(None)
+    ]
 
 
 def test_get_fix_no_project():
@@ -62,24 +72,33 @@ def test_get_fix_no_project():
 
 
 def test_get_fix_no_model():
-    assert Fix.get_fixes('CMIP5', 'BAD_MODEL', 'Amon', 'ch4') == []
+    assert Fix.get_fixes('CMIP5', 'BAD_MODEL', 'Amon', 'ch4') == [
+        GenericFix(None)
+    ]
 
 
 def test_get_fix_no_var():
-    assert Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'BAD_VAR') == []
+    assert Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'BAD_VAR') == [
+        GenericFix(None)
+    ]
 
 
 def test_get_fix_only_mip():
-    assert Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'thetao') == [Omon(None)]
+    assert Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'thetao') == [
+        Omon(None), GenericFix(None)
+    ]
 
 
 def test_get_fix_only_mip_case_insensitive():
-    assert Fix.get_fixes('CMIP6', 'CESM2', 'omOn', 'thetao') == [Omon(None)]
+    assert Fix.get_fixes('CMIP6', 'CESM2', 'omOn', 'thetao') == [
+        Omon(None), GenericFix(None)
+    ]
 
 
 def test_get_fix_mip_and_var():
-    assert (Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'tos') ==
-            [Tos(None), Omon(None)])
+    assert Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'tos') == [
+        Tos(None), Omon(None), GenericFix(None)
+    ]
 
 
 def test_fix_metadata():
@@ -156,3 +175,25 @@ def test_session():
     session = CFG.start_session('my session')
     fix = Fix(None, session=session)
     assert fix.session == session
+
+
+def test_frequency_empty():
+    fix = Fix(None)
+    assert fix.frequency is None
+
+
+def test_frequency_from_vardef():
+    vardef = get_var_info('CMIP6', 'Amon', 'tas')
+    fix = Fix(vardef)
+    assert fix.frequency == 'mon'
+
+
+def test_frequency_given():
+    fix = Fix(None, frequency='1hr')
+    assert fix.frequency == '1hr'
+
+
+def test_frequency_not_from_vardef():
+    vardef = get_var_info('CMIP6', 'Amon', 'tas')
+    fix = Fix(vardef, frequency='3hr')
+    assert fix.frequency == '3hr'
diff --git a/tests/integration/cmor/test_fix.py b/tests/integration/cmor/test_fix.py
new file mode 100644
index 000000000..e52dc0fd4
--- /dev/null
+++ b/tests/integration/cmor/test_fix.py
@@ -0,0 +1,909 @@
+"""Integration tests for fixes."""
+
+import dask.array as da
+import numpy as np
+import pytest
+from cf_units import Unit
+from iris.aux_factory import HybridHeightFactory, HybridPressureFactory
+from iris.coords import AuxCoord, DimCoord
+from iris.cube import Cube, CubeList
+
+from esmvalcore.cmor.check import CheckLevels, CMORCheckError
+from esmvalcore.exceptions import ESMValCoreDeprecationWarning
+from esmvalcore.preprocessor import (
+    cmor_check_data,
+    cmor_check_metadata,
+    fix_data,
+    fix_metadata,
+)
+
+
+# TODO: remove in v2.12
+@pytest.fixture(autouse=True)
+def disable_fix_cmor_checker(mocker):
+    """Disable the CMOR checker in fixes (will be default in v2.12)."""
+    class MockChecker:
+
+        def __init__(self, cube):
+            self._cube = cube
+
+        def check_metadata(self):
+            return self._cube
+
+        def check_data(self):
+            return self._cube
+
+    mock = mocker.patch('esmvalcore.cmor.fix._get_cmor_checker')
+    mock.return_value = MockChecker
+
+
+class TestGenericFix:
+    """Tests for ``GenericFix``."""
+
+    @pytest.fixture(autouse=True)
+    def setup(self, mocker):
+        """Setup tests."""
+        self.mock_debug = mocker.patch(
+            'esmvalcore.cmor._fixes.fix.GenericFix._debug_msg', autospec=True
+        )
+        self.mock_warning = mocker.patch(
+            'esmvalcore.cmor._fixes.fix.GenericFix._warning_msg',
+            autospec=True,
+        )
+
+        # Create sample data with CMOR errors
+        time_coord = DimCoord(
+            [15, 45],
+            standard_name='time',
+            var_name='time',
+            units=Unit('days since 1851-01-01', calendar='noleap'),
+            attributes={'test': 1, 'time_origin': 'will_be_removed'},
+        )
+        plev_coord_rev = DimCoord(
+            [250, 500, 850],
+            standard_name='air_pressure',
+            var_name='plev',
+            units='hPa',
+        )
+        lev_coord_hybrid_height = DimCoord(
+            [1.0, 0.5, 0.0],
+            standard_name='atmosphere_hybrid_height_coordinate',
+            var_name='lev',
+            units='m',
+        )
+        lev_coord_hybrid_pressure = DimCoord(
+            [0.0, 0.5, 1.0],
+            standard_name='atmosphere_hybrid_sigma_pressure_coordinate',
+            var_name='lev',
+            units='1',
+        )
+        ap_coord = AuxCoord(
+            [0.0, 0.0, 0.0],
+            var_name='ap',
+            units='Pa',
+        )
+        b_coord = AuxCoord(
+            [0.0, 0.5, 1.0],
+            var_name='b',
+            units='1',
+        )
+        ps_coord = AuxCoord(
+            np.full((2, 2, 2), 10),
+            var_name='ps',
+            units='Pa',
+        )
+        orog_coord = AuxCoord(
+            np.full((2, 2), 10),
+            var_name='orog',
+            units='m',
+        )
+        hybrid_height_factory = HybridHeightFactory(
+            delta=lev_coord_hybrid_height,
+            sigma=b_coord,
+            orography=orog_coord,
+        )
+        hybrid_pressure_factory = HybridPressureFactory(
+            delta=ap_coord,
+            sigma=lev_coord_hybrid_pressure,
+            surface_air_pressure=ps_coord,
+        )
+        lat_coord = DimCoord(
+            [0, 10],
+            standard_name='latitude',
+            var_name='lat',
+            units='degrees',
+        )
+        lat_coord_rev = DimCoord(
+            [10, -10],
+            standard_name='latitude',
+            var_name='lat',
+            units='degrees',
+        )
+        lat_coord_2d = AuxCoord(
+            [[10, -10]],
+            standard_name='latitude',
+            var_name='wrong_name',
+            units='degrees',
+        )
+        lon_coord = DimCoord(
+            [-180, 0],
+            standard_name='longitude',
+            var_name='lon',
+            units='degrees',
+        )
+        lon_coord_unstructured = AuxCoord(
+            [-180, 0],
+            bounds=[[-200, -180, -160], [-20, 0, 20]],
+            standard_name='longitude',
+            var_name='lon',
+            units='degrees',
+        )
+        lon_coord_2d = AuxCoord(
+            [[370, 380]],
+            standard_name='longitude',
+            var_name='wrong_name',
+            units='degrees',
+        )
+        height2m_coord = AuxCoord(
+            2.0,
+            standard_name='height',
+            var_name='height',
+            units='m',
+        )
+
+        coord_spec_3d = [
+            (time_coord, 0),
+            (lat_coord, 1),
+            (lon_coord, 2),
+        ]
+        self.cube_3d = Cube(
+            da.arange(2 * 2 * 2, dtype=np.float32).reshape(2, 2, 2),
+            standard_name='air_pressure',
+            long_name='Air Pressure',
+            var_name='tas',
+            units='celsius',
+            dim_coords_and_dims=coord_spec_3d,
+            aux_coords_and_dims=[(height2m_coord, ())],
+            attributes={},
+        )
+
+        coord_spec_4d = [
+            (time_coord, 0),
+            (plev_coord_rev, 1),
+            (lat_coord_rev, 2),
+            (lon_coord, 3),
+        ]
+        cube_4d = Cube(
+            da.arange(2 * 3 * 2 * 2, dtype=np.float32).reshape(2, 3, 2, 2),
+            standard_name='air_pressure',
+            long_name='Air Pressure',
+            var_name='ta',
+            units='celsius',
+            dim_coords_and_dims=coord_spec_4d,
+            attributes={},
+        )
+        self.cubes_4d = CubeList([cube_4d])
+
+        coord_spec_hybrid_height_4d = [
+            (time_coord, 0),
+            (lev_coord_hybrid_height, 1),
+            (lat_coord_rev, 2),
+            (lon_coord, 3),
+        ]
+        aux_coord_spec_hybrid_height_4d = [
+            (b_coord, 1),
+            (orog_coord, (2, 3)),
+        ]
+        cube_hybrid_height_4d = Cube(
+            da.arange(2 * 3 * 2 * 2, dtype=np.float32).reshape(2, 3, 2, 2),
+            standard_name='air_pressure',
+            long_name='Air Pressure',
+            var_name='ta',
+            units='celsius',
+            dim_coords_and_dims=coord_spec_hybrid_height_4d,
+            aux_coords_and_dims=aux_coord_spec_hybrid_height_4d,
+            aux_factories=[hybrid_height_factory],
+            attributes={},
+        )
+        self.cubes_hybrid_height_4d = CubeList([cube_hybrid_height_4d])
+
+        coord_spec_hybrid_pressure_4d = [
+            (time_coord, 0),
+            (lev_coord_hybrid_pressure, 1),
+            (lat_coord_rev, 2),
+            (lon_coord, 3),
+        ]
+        aux_coord_spec_hybrid_pressure_4d = [
+            (ap_coord, 1),
+            (ps_coord, (0, 2, 3)),
+        ]
+        cube_hybrid_pressure_4d = Cube(
+            da.arange(2 * 3 * 2 * 2, dtype=np.float32).reshape(2, 3, 2, 2),
+            standard_name='air_pressure',
+            long_name='Air Pressure',
+            var_name='ta',
+            units='celsius',
+            dim_coords_and_dims=coord_spec_hybrid_pressure_4d,
+            aux_coords_and_dims=aux_coord_spec_hybrid_pressure_4d,
+            aux_factories=[hybrid_pressure_factory],
+            attributes={},
+        )
+        self.cubes_hybrid_pressure_4d = CubeList([cube_hybrid_pressure_4d])
+
+        coord_spec_unstrucutred = [
+            (height2m_coord, ()),
+            (lat_coord_rev, 1),
+            (lon_coord_unstructured, 1),
+        ]
+        cube_unstructured = Cube(
+            da.zeros((2, 2)),
+            standard_name='air_pressure',
+            long_name='Air Pressure',
+            var_name='tas',
+            units='celsius',
+            dim_coords_and_dims=[(time_coord, 0)],
+            aux_coords_and_dims=coord_spec_unstrucutred,
+            attributes={},
+        )
+        self.cubes_unstructured = CubeList([cube_unstructured])
+
+        coord_spec_2d = [
+            (height2m_coord, ()),
+            (lat_coord_2d, (1, 2)),
+            (lon_coord_2d, (1, 2)),
+        ]
+        cube_2d_latlon = Cube(
+            da.zeros((2, 1, 2)),
+            standard_name='air_pressure',
+            long_name='Air Pressure',
+            var_name='tas',
+            units='celsius',
+            dim_coords_and_dims=[(time_coord, 0)],
+            aux_coords_and_dims=coord_spec_2d,
+            attributes={},
+        )
+        self.cubes_2d_latlon = CubeList([cube_2d_latlon])
+
+    def assert_time_metadata(self, cube):
+        """Assert time metadata is correct."""
+        assert cube.coord('time').standard_name == 'time'
+        assert cube.coord('time').var_name == 'time'
+        assert cube.coord('time').units == Unit(
+            'days since 1850-01-01', calendar='365_day'
+        )
+        assert cube.coord('time').attributes == {'test': 1}
+
+    def assert_time_data(self, cube, time_has_bounds=True):
+        """Assert time data is correct."""
+        np.testing.assert_allclose(cube.coord('time').points, [380, 410])
+        if time_has_bounds:
+            np.testing.assert_allclose(
+                cube.coord('time').bounds, [[365, 396], [396, 424]],
+            )
+        else:
+            assert cube.coord('time').bounds is None
+
+    def assert_plev_metadata(self, cube):
+        """Assert plev metadata is correct."""
+        assert cube.coord('air_pressure').standard_name == 'air_pressure'
+        assert cube.coord('air_pressure').var_name == 'plev'
+        assert cube.coord('air_pressure').units == 'Pa'
+        assert cube.coord('air_pressure').attributes == {}
+
+    def assert_lat_metadata(self, cube):
+        """Assert lat metadata is correct."""
+        assert cube.coord('latitude').standard_name == 'latitude'
+        assert cube.coord('latitude').var_name == 'lat'
+        assert str(cube.coord('latitude').units) == 'degrees_north'
+        assert cube.coord('latitude').attributes == {}
+
+    def assert_lon_metadata(self, cube):
+        """Assert lon metadata is correct."""
+        assert cube.coord('longitude').standard_name == 'longitude'
+        assert cube.coord('longitude').var_name == 'lon'
+        assert str(cube.coord('longitude').units) == 'degrees_east'
+        assert cube.coord('longitude').attributes == {}
+
+    def assert_ta_metadata(self, cube):
+        """Assert ta metadata is correct."""
+        # Variable metadata
+        assert cube.standard_name == 'air_temperature'
+        assert cube.long_name == 'Air Temperature'
+        assert cube.var_name == 'ta'
+        assert cube.units == 'K'
+        assert cube.attributes == {}
+
+    def assert_ta_data(self, cube, time_has_bounds=True):
+        """Assert ta data is correct."""
+        assert cube.has_lazy_data()
+        np.testing.assert_allclose(
+            cube.data,
+            [[[[284.15, 283.15],
+               [282.15, 281.15]],
+              [[280.15, 279.15],
+               [278.15, 277.15]],
+              [[276.15, 275.15],
+               [274.15, 273.15]]],
+             [[[296.15, 295.15],
+               [294.15, 293.15]],
+              [[292.15, 291.15],
+               [290.15, 289.15]],
+              [[288.15, 287.15],
+               [286.15, 285.15]]]],
+        )
+
+        # Time
+        self.assert_time_data(cube, time_has_bounds=time_has_bounds)
+
+        # Air pressure
+        np.testing.assert_allclose(
+            cube.coord('air_pressure').points,
+            [85000.0, 50000.0, 25000.0],
+            atol=1e-8,
+        )
+        assert cube.coord('air_pressure').bounds is None
+
+        # Latitude
+        np.testing.assert_allclose(
+            cube.coord('latitude').points, [-10.0, 10.0]
+        )
+        np.testing.assert_allclose(
+            cube.coord('latitude').bounds, [[-20.0, 0.0], [0.0, 20.0]]
+        )
+
+        # Longitude
+        np.testing.assert_allclose(
+            cube.coord('longitude').points, [0.0, 180.0]
+        )
+        np.testing.assert_allclose(
+            cube.coord('longitude').bounds, [[-90.0, 90.0], [90.0, 270.0]]
+        )
+
+    def assert_tas_metadata(self, cube):
+        """Assert tas metadata is correct."""
+        assert cube.standard_name == 'air_temperature'
+        assert cube.long_name == 'Near-Surface Air Temperature'
+        assert cube.var_name == 'tas'
+        assert cube.units == 'K'
+        assert cube.attributes == {}
+
+        # Height 2m coordinate
+        assert cube.coord('height').standard_name == 'height'
+        assert cube.coord('height').var_name == 'height'
+        assert cube.coord('height').units == 'm'
+        assert cube.coord('height').attributes == {}
+        np.testing.assert_allclose(cube.coord('height').points, 2.0)
+        assert cube.coord('height').bounds is None
+
+    def test_fix_metadata_amon_ta(self):
+        """Test ``fix_metadata``."""
+        short_name = 'ta'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'Amon'
+
+        fixed_cubes = fix_metadata(
+            self.cubes_4d,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_ta_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_plev_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+        self.assert_ta_data(fixed_cube)
+
+        cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 3
+        assert self.mock_warning.call_count == 9
+
+    def test_fix_metadata_amon_ta_wrong_lat_units(self):
+        """Test ``fix_metadata``."""
+        short_name = 'ta'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'Amon'
+
+        # Change units of latitude
+        self.cubes_4d[0].coord('latitude').units = 'K'
+
+        fixed_cubes = fix_metadata(
+            self.cubes_4d,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_ta_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_plev_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+        self.assert_ta_data(fixed_cube)
+
+        # CMOR check will fail because of wrong latitude units
+        assert fixed_cube.coord('latitude').units == 'K'
+        with pytest.raises(CMORCheckError):
+            cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        print(self.mock_debug.mock_calls)
+        print(self.mock_warning.mock_calls)
+
+        assert self.mock_debug.call_count == 3
+        assert self.mock_warning.call_count == 9
+
+    def test_fix_metadata_cfmon_ta_hybrid_height(self):
+        """Test ``fix_metadata`` with hybrid height coordinate."""
+        short_name = 'ta'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'CFmon'
+
+        fixed_cubes = fix_metadata(
+            self.cubes_hybrid_height_4d,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        hybrid_coord = fixed_cube.coord('atmosphere_hybrid_height_coordinate')
+        assert hybrid_coord.var_name == 'lev'
+        assert hybrid_coord.long_name is None
+        assert hybrid_coord.units == 'm'
+        np.testing.assert_allclose(hybrid_coord.points, [0.0, 0.5, 1.0])
+        assert fixed_cube.coords('altitude')
+        assert fixed_cube.coord_dims('altitude') == (1, 2, 3)
+
+        self.assert_ta_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+
+        cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 4
+        assert self.mock_warning.call_count == 9
+
+    def test_fix_metadata_cfmon_ta_hybrid_pressure(self):
+        """Test ``fix_metadata`` with hybrid pressure coordinate."""
+        short_name = 'ta'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'CFmon'
+
+        fixed_cubes = fix_metadata(
+            self.cubes_hybrid_pressure_4d,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        hybrid_coord = fixed_cube.coord(
+            'atmosphere_hybrid_sigma_pressure_coordinate'
+        )
+        assert hybrid_coord.var_name == 'lev'
+        assert hybrid_coord.long_name is None
+        assert hybrid_coord.units == '1'
+        np.testing.assert_allclose(hybrid_coord.points, [1.0, 0.5, 0.0])
+        assert fixed_cube.coords('air_pressure')
+        assert fixed_cube.coord_dims('air_pressure') == (0, 1, 2, 3)
+
+        self.assert_ta_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+
+        cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 4
+        assert self.mock_warning.call_count == 9
+
+    def test_fix_metadata_cfmon_ta_alternative(self):
+        """Test ``fix_metadata`` with alternative generic level coordinate."""
+        short_name = 'ta'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'CFmon'
+
+        fixed_cubes = fix_metadata(
+            self.cubes_4d,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_ta_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_plev_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+        self.assert_ta_data(fixed_cube)
+
+        cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 4
+        assert self.mock_warning.call_count == 9
+
+    def test_fix_metadata_cfmon_ta_no_alternative(self, mocker):
+        """Test ``fix_metadata`` with  no alternative coordinate."""
+        short_name = 'ta'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'CFmon'
+
+        # Remove alternative coordinate
+        self.cubes_4d[0].remove_coord('air_pressure')
+
+        fixed_cubes = fix_metadata(
+            self.cubes_4d,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_ta_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_time_data(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+
+        # CMOR check will fail because of missing alevel coordinate
+        assert not fixed_cube.coords('air_pressure')
+        with pytest.raises(CMORCheckError):
+            cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 2
+        assert self.mock_warning.call_count == 8
+
+    def test_fix_metadata_e1hr_ta(self):
+        """Test ``fix_metadata`` with plev3."""
+        short_name = 'ta'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'E1hr'
+
+        # Slightly adapt plev to test fixing of requested levels
+        self.cubes_4d[0].coord('air_pressure').points = [
+            250.0 + 9e-8, 500.0 + 9e-8, 850.0 + 9e-8
+        ]
+
+        fixed_cubes = fix_metadata(
+            self.cubes_4d,
+            short_name,
+            project,
+            dataset,
+            mip,
+            frequency='mon',
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_ta_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_plev_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+        self.assert_ta_data(fixed_cube, time_has_bounds=False)
+
+        cmor_check_metadata(
+            fixed_cube, project, mip, short_name, frequency='mon'
+        )
+
+        assert self.mock_debug.call_count == 4
+        assert self.mock_warning.call_count == 8
+
+    def test_fix_metadata_amon_tas_unstructured(self):
+        """Test ``fix_metadata`` with unstructured grid."""
+        short_name = 'tas'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'Amon'
+
+        fixed_cubes = fix_metadata(
+            self.cubes_unstructured,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_tas_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+
+        # Latitude
+        np.testing.assert_allclose(
+            fixed_cube.coord('latitude').points, [10.0, -10.0]
+        )
+        assert fixed_cube.coord('latitude').bounds is None
+
+        # Longitude
+        np.testing.assert_allclose(
+            fixed_cube.coord('longitude').points, [180.0, 0.0]
+        )
+        np.testing.assert_allclose(
+            fixed_cube.coord('longitude').bounds,
+            [[160.0, 180.0, 200.0], [340.0, 0.0, 20.0]],
+        )
+
+        # Variable data
+        assert fixed_cube.has_lazy_data()
+        np.testing.assert_allclose(
+            fixed_cube.data, [[273.15, 273.15], [273.15, 273.15]]
+        )
+
+        cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 2
+        assert self.mock_warning.call_count == 6
+
+    def test_fix_metadata_amon_tas_2d_latlon(self):
+        """Test ``fix_metadata`` with 2D latitude/longitude."""
+        short_name = 'tas'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'Amon'
+
+        fixed_cubes = fix_metadata(
+            self.cubes_2d_latlon,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_tas_metadata(fixed_cube)
+        self.assert_time_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+
+        # Latitude
+        np.testing.assert_allclose(
+            fixed_cube.coord('latitude').points, [[10.0, -10.0]]
+        )
+        assert fixed_cube.coord('latitude').bounds is None
+
+        # Longitude
+        np.testing.assert_allclose(
+            fixed_cube.coord('longitude').points, [[10.0, 20.0]]
+        )
+        assert fixed_cube.coord('longitude').bounds is None
+
+        # Variable data
+        assert fixed_cube.has_lazy_data()
+        np.testing.assert_allclose(
+            fixed_cube.data, [[[273.15, 273.15]], [[273.15, 273.15]]]
+        )
+
+        cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 3
+        assert self.mock_warning.call_count == 8
+
+    def test_fix_metadata_amon_tas_invalid_time_units(self):
+        """Test ``fix_metadata`` with invalid time units."""
+        short_name = 'tas'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'Amon'
+
+        self.cubes_2d_latlon[0].remove_coord('time')
+        aux_time_coord = AuxCoord(
+            [1, 2],
+            standard_name='time',
+            var_name='time',
+            units='kg',
+        )
+        self.cubes_2d_latlon[0].add_aux_coord(aux_time_coord, 0)
+
+        fixed_cubes = fix_metadata(
+            self.cubes_2d_latlon,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+
+        assert fixed_cube.coord('time').units == 'kg'
+
+        # CMOR checks fail because calendar is not defined
+        with pytest.raises(ValueError):
+            cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 3
+        assert self.mock_warning.call_count == 7
+
+    def test_fix_metadata_amon_tas_invalid_time_attrs(self):
+        """Test ``fix_metadata`` with invalid time attributes."""
+        short_name = 'tas'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'Amon'
+
+        self.cubes_2d_latlon[0].attributes = {
+            'parent_time_units': 'this is certainly not a unit',
+            'branch_time_in_parent': 'BRANCH TIME IN PARENT',
+            'branch_time_in_child': 'BRANCH TIME IN CHILD',
+        }
+
+        fixed_cubes = fix_metadata(
+            self.cubes_2d_latlon,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        self.assert_time_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+
+        assert fixed_cube.attributes == {
+            'parent_time_units': 'this is certainly not a unit',
+            'branch_time_in_parent': 'BRANCH TIME IN PARENT',
+            'branch_time_in_child': 'BRANCH TIME IN CHILD',
+        }
+
+        cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 3
+        assert self.mock_warning.call_count == 8
+
+    def test_fix_metadata_oimon_ssi(self):
+        """Test ``fix_metadata`` with psu units."""
+        short_name = 'ssi'
+        project = 'CMIP5'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'OImon'
+
+        self.cubes_2d_latlon[0].var_name = 'ssi'
+        self.cubes_2d_latlon[0].attributes = {
+            'invalid_units': 'psu',
+            'parent_time_units': 'no parent',
+        }
+
+        # Also test 2D longitude that already has bounds
+        self.cubes_2d_latlon[0].coord('latitude').var_name = 'lat'
+        self.cubes_2d_latlon[0].coord('longitude').var_name = 'lon'
+        self.cubes_2d_latlon[0].coord('longitude').bounds = [
+            [[365.0, 375.0], [375.0, 400.0]]
+        ]
+
+        fixed_cubes = fix_metadata(
+            self.cubes_2d_latlon,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert len(fixed_cubes) == 1
+        fixed_cube = fixed_cubes[0]
+
+        # Variable metadata
+        assert fixed_cube.standard_name == 'sea_ice_salinity'
+        assert fixed_cube.long_name == 'Sea Ice Salinity'
+        assert fixed_cube.var_name == 'ssi'
+        assert fixed_cube.units == '1'
+        assert fixed_cube.attributes == {'parent_time_units': 'no parent'}
+
+        # Coordinates
+        self.assert_time_metadata(fixed_cube)
+        self.assert_lat_metadata(fixed_cube)
+        self.assert_lon_metadata(fixed_cube)
+
+        # Latitude
+        np.testing.assert_allclose(
+            fixed_cube.coord('latitude').points, [[10.0, -10.0]]
+        )
+        assert fixed_cube.coord('latitude').bounds is None
+
+        # Longitude
+        np.testing.assert_allclose(
+            fixed_cube.coord('longitude').points, [[10.0, 20.0]]
+        )
+        np.testing.assert_allclose(
+            fixed_cube.coord('longitude').bounds,
+            [[[5.0, 15.0], [15.0, 40.0]]],
+        )
+
+        # Variable data
+        assert fixed_cube.has_lazy_data()
+        np.testing.assert_allclose(
+            fixed_cube.data, [[[0.0, 0.0]], [[0.0, 0.0]]],
+        )
+
+        cmor_check_metadata(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 2
+        assert self.mock_warning.call_count == 6
+
+    def test_fix_data_amon_tas(self):
+        """Test ``fix_data``."""
+        short_name = 'tas'
+        project = 'CMIP6'
+        dataset = '__MODEL_WITH_NO_EXPLICIT_FIX__'
+        mip = 'Amon'
+
+        fixed_cube = fix_data(
+            self.cube_3d,
+            short_name,
+            project,
+            dataset,
+            mip,
+        )
+
+        assert fixed_cube.has_lazy_data()
+
+        cmor_check_data(fixed_cube, project, mip, short_name)
+
+        assert self.mock_debug.call_count == 0
+        assert self.mock_warning.call_count == 0
+
+    def test_deprecate_check_level_fix_metadata(self):
+        """Test deprecation of check level in ``fix_metadata``."""
+        with pytest.warns(ESMValCoreDeprecationWarning):
+            fix_metadata(
+                self.cubes_4d,
+                'ta',
+                'CMIP6',
+                'MODEL',
+                'Amon',
+                check_level=CheckLevels.RELAXED,
+            )
+
+    def test_deprecate_check_level_fix_data(self):
+        """Test deprecation of check level in ``fix_data``."""
+        with pytest.warns(ESMValCoreDeprecationWarning):
+            fix_metadata(
+                self.cubes_4d,
+                'ta',
+                'CMIP6',
+                'MODEL',
+                'Amon',
+                check_level=CheckLevels.RELAXED,
+            )
diff --git a/tests/integration/cmor/test_table.py b/tests/integration/cmor/test_table.py
index 7b25b8d71..8b8d63e57 100644
--- a/tests/integration/cmor/test_table.py
+++ b/tests/integration/cmor/test_table.py
@@ -3,6 +3,8 @@
 import os
 import unittest
 
+import pytest
+
 import esmvalcore.cmor
 from esmvalcore.cmor.table import (
     CMIP3Info,
@@ -10,6 +12,7 @@ from esmvalcore.cmor.table import (
     CMIP6Info,
     CustomInfo,
     _update_cmor_facets,
+    get_var_info,
 )
 
 
@@ -485,3 +488,41 @@ class TestCustomInfo(unittest.TestCase):
         self.assertEqual(var.long_name,
                          'Atmosphere CH4 surface')
         self.assertEqual(var.units, '1e-09')
+
+
+@pytest.mark.parametrize(
+    'project,mip,short_name,frequency',
+    [
+        ('CMIP5', 'Amon', 'tas', 'mon'),
+        ('CMIP5', 'day', 'tas', 'day'),
+        ('CMIP6', 'Amon', 'tas', 'mon'),
+        ('CMIP6', 'day', 'tas', 'day'),
+        ('CORDEX', '3hr', 'tas', '3hr'),
+    ]
+)
+def test_get_var_info(project, mip, short_name, frequency):
+    """Test ``get_var_info``."""
+    var_info = get_var_info(project, mip, short_name)
+
+    assert var_info.short_name == short_name
+    assert var_info.frequency == frequency
+
+
+@pytest.mark.parametrize(
+    'mip,short_name',
+    [
+        ('INVALID_MIP', 'tas'),
+        ('Amon', 'INVALID_VAR'),
+    ]
+)
+def test_get_var_info_invalid_mip_short_name(mip, short_name):
+    """Test ``get_var_info``."""
+    var_info = get_var_info('CMIP6', mip, short_name)
+
+    assert var_info is None
+
+
+def test_get_var_info_invalid_project():
+    """Test ``get_var_info``."""
+    with pytest.raises(KeyError):
+        get_var_info('INVALID_PROJECT', 'Amon', 'tas')
diff --git a/tests/unit/cmor/test_cmor_check.py b/tests/unit/cmor/test_cmor_check.py
index f3135558a..957136fd4 100644
--- a/tests/unit/cmor/test_cmor_check.py
+++ b/tests/unit/cmor/test_cmor_check.py
@@ -1,5 +1,6 @@
 """Unit tests for the CMORCheck class."""
 
+import logging
 import unittest
 from copy import deepcopy
 
@@ -10,6 +11,7 @@ import iris.coords
 import iris.cube
 import iris.util
 import numpy as np
+import pytest
 from cf_units import Unit
 
 from esmvalcore.cmor.check import (
@@ -18,6 +20,9 @@ from esmvalcore.cmor.check import (
     CMORCheckError,
     _get_cmor_checker,
 )
+from esmvalcore.exceptions import ESMValCoreDeprecationWarning
+
+logger = logging.getLogger(__name__)
 
 
 class VariableInfoMock:
@@ -142,33 +147,39 @@ class TestCMORCheck(unittest.TestCase):
         """Test checks succeeds for a good cube."""
         self._check_cube()
 
-    def _check_cube(self, automatic_fixes=False, frequency=None,
+    def _check_cube(self, frequency=None,
                     check_level=CheckLevels.DEFAULT):
-        """Apply checks and optionally automatic fixes to self.cube."""
+        """Apply checks to self.cube."""
         def checker(cube):
             return CMORCheck(
                 cube,
                 self.var_info,
-                automatic_fixes=automatic_fixes,
                 frequency=frequency,
                 check_level=check_level)
 
         self.cube = checker(self.cube).check_metadata()
         self.cube = checker(self.cube).check_data()
 
-    def _check_cube_metadata(self, automatic_fixes=False, frequency=None,
+    def _check_cube_metadata(self, frequency=None,
                              check_level=CheckLevels.DEFAULT):
-        """Apply checks and optionally automatic fixes to self.cube."""
+        """Apply checks to self.cube."""
         def checker(cube):
             return CMORCheck(
                 cube,
                 self.var_info,
-                automatic_fixes=automatic_fixes,
                 frequency=frequency,
                 check_level=check_level)
 
         self.cube = checker(self.cube).check_metadata()
 
+    def test_check_with_custom_logger(self):
+        """Test checks with custom logger."""
+        def checker(cube):
+            return CMORCheck(cube, self.var_info)
+
+        self.cube = checker(self.cube).check_metadata(logger=logger)
+        self.cube = checker(self.cube).check_data(logger=logger)
+
     def test_check_with_month_number(self):
         """Test checks succeeds for a good cube with month number."""
         iris.coord_categorisation.add_month_number(self.cube, 'time')
@@ -189,13 +200,6 @@ class TestCMORCheck(unittest.TestCase):
         iris.coord_categorisation.add_year(self.cube, 'time')
         self._check_cube()
 
-    def test_check_with_time_attributes(self):
-        """Test checks succeeds for a good cube with year."""
-        self.cube.coord('time').attributes['time_origin'] = "cow"
-        assert self.cube.coord('time').attributes['time_origin'] == "cow"
-        self._check_cube()
-        assert 'time_origin' not in self.cube.coord('time').attributes
-
     def test_check_no_multiple_coords_same_stdname(self):
         """Test checks fails if two coords have the same standard_name."""
         self.cube.add_aux_coord(
@@ -209,43 +213,20 @@ class TestCMORCheck(unittest.TestCase):
         )
         self._check_fails_in_metadata()
 
-    def test_check_bad_standard_name_auto_fix(self):
-        """Test check pass for a bad standard_name with automatic fixes."""
-        self.cube.standard_name = 'wind_speed'
-        self._check_cube(automatic_fixes=True)
-        self._check_cube()
-
     def test_check_bad_standard_name(self):
         """Test check fails for a bad short_name."""
         self.cube.standard_name = 'wind_speed'
-        self._check_fails_in_metadata(automatic_fixes=False)
-
-    def test_check_bad_long_name_auto_fix(self):
-        """Test check pass for a bad standard_name with automatic fixes."""
-        self.cube.long_name = 'bad_name'
-        self._check_cube(automatic_fixes=True)
-        self._check_cube()
-
-    def test_check_bad_long_name_auto_fix_report_warning(self):
-        """Test check pass for a bad standard_name with automatic fixes."""
-        self.cube.long_name = 'bad_name'
-        self._check_warnings_on_metadata(automatic_fixes=True)
+        self._check_fails_in_metadata()
 
     def test_check_bad_long_name(self):
         """Test check fails for a bad short_name."""
         self.cube.long_name = 'bad_name'
         self._check_fails_in_metadata()
 
-    def test_check_with_unit_conversion(self):
-        """Test check succeeds for a good cube requiring unit conversion."""
+    def test_check_bad_units(self):
+        """Test check fails for bad units."""
         self.cube.units = 'days'
-        self._check_cube()
-
-    def test_check_with_psu_units(self):
-        """Test check succeeds for a good cube with psu units."""
-        self.var_info.units = 'psu'
-        self.cube = self.get_cube(self.var_info)
-        self._check_cube(automatic_fixes=True)
+        self._check_fails_in_metadata()
 
     def test_check_with_positive(self):
         """Check variable with positive attribute."""
@@ -288,25 +269,6 @@ class TestCMORCheck(unittest.TestCase):
         self.cube = self._get_unstructed_grid_cube()
         self._check_cube()
 
-    def test_unstructured_grid_automatic_fixes(self):
-        """Check succeeds even if two required coordinates share dimensions."""
-        self.cube = self._get_unstructed_grid_cube(correct_lon=False,
-                                                   n_bounds=3)
-
-        lon_coord = self.cube.coord('longitude')
-        np.testing.assert_allclose(lon_coord.points.min(), -180.0)
-        np.testing.assert_allclose(lon_coord.points.max(), 162.0)
-        np.testing.assert_allclose(lon_coord.bounds.min(), -180.0)
-        np.testing.assert_allclose(lon_coord.bounds.max(), 171.0)
-
-        self._check_cube(automatic_fixes=True)
-
-        lon_coord = self.cube.coord('longitude')
-        np.testing.assert_allclose(lon_coord.points.min(), 0.0)
-        np.testing.assert_allclose(lon_coord.points.max(), 342.0)
-        np.testing.assert_allclose(lon_coord.bounds.min(), 0.0)
-        np.testing.assert_allclose(lon_coord.bounds.max(), 351.0)
-
     def test_bad_generic_level(self):
         """Test check fails in metadata if generic level coord
         has wrong var_name."""
@@ -328,6 +290,14 @@ class TestCMORCheck(unittest.TestCase):
         checker.check_metadata()
         checker.check_data()
 
+    # TODO: remove in v2.12
+    def test_valid_generic_level_automatic_fixes(self):
+        """Test valid generic level coordinate with automatic fixes."""
+        self._setup_generic_level_var()
+        checker = CMORCheck(self.cube, self.var_info, automatic_fixes=True)
+        checker.check_metadata()
+        checker.check_data()
+
     def test_invalid_generic_level(self):
         """Test invalid generic level coordinate."""
         self._setup_generic_level_var()
@@ -376,19 +346,19 @@ class TestCMORCheck(unittest.TestCase):
         """Test check fails for a bad variable standard_name with
         --cmor-check strict."""
         self.cube.standard_name = 'wind_speed'
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_bad_var_long_name_strict_flag(self):
         """Test check fails for a bad variable long_name with
         --cmor-check strict."""
         self.cube.long_name = "Near-Surface Wind Speed"
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_bad_var_units_strict_flag(self):
         """Test check fails for a bad variable units with
         --cmor-check strict."""
         self.cube.units = "kg"
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_bad_attributes_strict_flag(self):
         """Test check fails for a bad variable attribute with
@@ -397,7 +367,7 @@ class TestCMORCheck(unittest.TestCase):
         self.var_info.positive = "up"
         self.cube = self.get_cube(self.var_info)
         self.cube.attributes['positive'] = "Wrong attribute"
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_bad_rank_strict_flag(self):
         """Test check fails for a bad variable rank with
@@ -405,32 +375,32 @@ class TestCMORCheck(unittest.TestCase):
         lat = iris.coords.AuxCoord.from_coord(self.cube.coord('latitude'))
         self.cube.remove_coord('latitude')
         self.cube.add_aux_coord(lat, self.cube.coord_dims('longitude'))
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_bad_coord_var_name_strict_flag(self):
         """Test check fails for bad coord var_name with
         --cmor-check strict"""
         self.var_info.table_type = 'CMIP5'
         self.cube.coord('longitude').var_name = 'bad_name'
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_missing_lon_strict_flag(self):
         """Test check fails for missing longitude with --cmor-check strict"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('longitude')
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_missing_lat_strict_flag(self):
         """Test check fails for missing latitude with --cmor-check strict"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('latitude')
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_missing_time_strict_flag(self):
         """Test check fails for missing time with --cmor-check strict"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('time')
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_missing_coord_strict_flag(self):
         """Test check fails for missing coord other than lat and lon
@@ -438,28 +408,25 @@ class TestCMORCheck(unittest.TestCase):
         self.var_info.coordinates.update(
             {'height2m': CoordinateInfoMock('height2m')}
         )
-        self._check_fails_in_metadata(automatic_fixes=False)
+        self._check_fails_in_metadata()
 
     def test_check_bad_var_standard_name_relaxed_flag(self):
         """Test check reports warning for a bad variable standard_name with
         --cmor-check relaxed."""
         self.cube.standard_name = 'wind_speed'
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.RELAXED)
+        self._check_warnings_on_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_bad_var_long_name_relaxed_flag(self):
         """Test check reports warning for a bad variable long_name with
         --cmor-check relaxed."""
         self.cube.long_name = "Near-Surface Wind Speed"
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.RELAXED)
+        self._check_warnings_on_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_bad_var_units_relaxed_flag(self):
         """Test check reports warning for a bad variable units with
         --cmor-check relaxed."""
         self.cube.units = "kg"
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.RELAXED)
+        self._check_warnings_on_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_bad_attributes_relaxed_flag(self):
         """Test check report warnings for a bad variable attribute with
@@ -468,8 +435,7 @@ class TestCMORCheck(unittest.TestCase):
         self.var_info.positive = "up"
         self.cube = self.get_cube(self.var_info)
         self.cube.attributes['positive'] = "Wrong attribute"
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.RELAXED)
+        self._check_warnings_on_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_bad_rank_relaxed_flag(self):
         """Test check report warnings for a bad variable rank with
@@ -477,37 +443,32 @@ class TestCMORCheck(unittest.TestCase):
         lat = iris.coords.AuxCoord.from_coord(self.cube.coord('latitude'))
         self.cube.remove_coord('latitude')
         self.cube.add_aux_coord(lat, self.cube.coord_dims('longitude'))
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.RELAXED)
+        self._check_warnings_on_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_bad_coord_standard_name_relaxed_flag(self):
         """Test check reports warning for bad coord var_name with
         --cmor-check relaxed"""
         self.var_info.table_type = 'CMIP5'
         self.cube.coord('longitude').var_name = 'bad_name'
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.RELAXED)
+        self._check_warnings_on_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_missing_lon_relaxed_flag(self):
         """Test check fails for missing longitude with --cmor-check relaxed"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('longitude')
-        self._check_fails_in_metadata(automatic_fixes=False,
-                                      check_level=CheckLevels.RELAXED)
+        self._check_fails_in_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_missing_lat_relaxed_flag(self):
         """Test check fails for missing latitude with --cmor-check relaxed"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('latitude')
-        self._check_fails_in_metadata(automatic_fixes=False,
-                                      check_level=CheckLevels.RELAXED)
+        self._check_fails_in_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_missing_time_relaxed_flag(self):
         """Test check fails for missing latitude with --cmor-check relaxed"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('time')
-        self._check_fails_in_metadata(automatic_fixes=False,
-                                      check_level=CheckLevels.RELAXED)
+        self._check_fails_in_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_missing_coord_relaxed_flag(self):
         """Test check reports warning for missing coord other than lat and lon
@@ -515,29 +476,25 @@ class TestCMORCheck(unittest.TestCase):
         self.var_info.coordinates.update(
             {'height2m': CoordinateInfoMock('height2m')}
         )
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.RELAXED)
+        self._check_warnings_on_metadata(check_level=CheckLevels.RELAXED)
 
     def test_check_bad_var_standard_name_none_flag(self):
         """Test check reports warning for a bad variable standard_name with
         --cmor-check ignore."""
         self.cube.standard_name = 'wind_speed'
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_bad_var_long_name_none_flag(self):
         """Test check reports warning for a bad variable long_name with
         --cmor-check ignore."""
         self.cube.long_name = "Near-Surface Wind Speed"
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_bad_var_units_none_flag(self):
         """Test check reports warning for a bad variable unit with
         --cmor-check ignore."""
         self.cube.units = "kg"
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_bad_attributes_none_flag(self):
         """Test check reports warning for a bad variable attribute with
@@ -546,8 +503,7 @@ class TestCMORCheck(unittest.TestCase):
         self.var_info.positive = "up"
         self.cube = self.get_cube(self.var_info)
         self.cube.attributes['positive'] = "Wrong attribute"
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_bad_rank_none_flag(self):
         """Test check reports warning for a bad variable rank with
@@ -555,40 +511,35 @@ class TestCMORCheck(unittest.TestCase):
         lat = iris.coords.AuxCoord.from_coord(self.cube.coord('latitude'))
         self.cube.remove_coord('latitude')
         self.cube.add_aux_coord(lat, self.cube.coord_dims('longitude'))
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_bad_coord_standard_name_none_flag(self):
         """Test check reports warning for bad coord var_name with
         --cmor-check ignore."""
         self.var_info.table_type = 'CMIP5'
         self.cube.coord('longitude').var_name = 'bad_name'
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_missing_lon_none_flag(self):
         """Test check reports warning for missing longitude with
         --cmor-check ignore"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('longitude')
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_missing_lat_none_flag(self):
         """Test check reports warning for missing latitude with
         --cmor-check ignore"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('latitude')
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_missing_time_none_flag(self):
         """Test check reports warning for missing time
         with --cmor-check ignore"""
         self.var_info.table_type = 'CMIP5'
         self.cube.remove_coord('time')
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_missing_coord_none_flag(self):
         """Test check reports warning for missing coord other than lat, lon and
@@ -596,8 +547,7 @@ class TestCMORCheck(unittest.TestCase):
         self.var_info.coordinates.update(
             {'height2m': CoordinateInfoMock('height2m')}
         )
-        self._check_warnings_on_metadata(automatic_fixes=False,
-                                         check_level=CheckLevels.IGNORE)
+        self._check_warnings_on_metadata(check_level=CheckLevels.IGNORE)
 
     def test_check_lazy(self):
         """Test checker does not realise data or aux_coords."""
@@ -627,29 +577,28 @@ class TestCMORCheck(unittest.TestCase):
         self.assertTrue(self.cube.coord('longitude').has_lazy_points())
         self.assertTrue(self.cube.has_lazy_data())
 
-    def _check_fails_in_metadata(self, automatic_fixes=False, frequency=None,
+    def _check_fails_in_metadata(self, frequency=None,
                                  check_level=CheckLevels.DEFAULT):
         checker = CMORCheck(
             self.cube,
             self.var_info,
-            automatic_fixes=automatic_fixes,
             frequency=frequency,
-            check_level=check_level)
+            check_level=check_level,
+        )
         with self.assertRaises(CMORCheckError):
             checker.check_metadata()
 
-    def _check_warnings_on_metadata(self, automatic_fixes=False,
-                                    check_level=CheckLevels.DEFAULT):
+    def _check_warnings_on_metadata(self, check_level=CheckLevels.DEFAULT):
         checker = CMORCheck(
-            self.cube, self.var_info, automatic_fixes=automatic_fixes,
-            check_level=check_level
+            self.cube, self.var_info, check_level=check_level
         )
         checker.check_metadata()
         self.assertTrue(checker.has_warnings())
 
-    def _check_debug_messages_on_metadata(self, automatic_fixes=False):
+    def _check_debug_messages_on_metadata(self):
         checker = CMORCheck(
-            self.cube, self.var_info, automatic_fixes=automatic_fixes)
+            self.cube, self.var_info,
+        )
         checker.check_metadata()
         self.assertTrue(checker.has_debug_messages())
 
@@ -667,20 +616,6 @@ class TestCMORCheck(unittest.TestCase):
         checker.check_metadata()
         self.assertTrue(checker.has_warnings())
 
-    def test_almost_requested(self):
-        """Automatically fix tiny deviations from the requested values."""
-        coord = self.cube.coord('air_pressure')
-        values = np.array(coord.points)
-        values[0] += 1.e-7
-        values[-1] += 1.e-7
-        self._update_coordinate_values(self.cube, coord, values)
-        checker = CMORCheck(self.cube, self.var_info, automatic_fixes=True)
-        checker.check_metadata()
-        reference = np.array(
-            self.var_info.coordinates['air_pressure'].requested, dtype=float)
-        np.testing.assert_array_equal(
-            self.cube.coord('air_pressure').points, reference)
-
     def test_requested_str_values(self):
         """
         Warning if requested values are not present.
@@ -728,65 +663,40 @@ class TestCMORCheck(unittest.TestCase):
         self._update_coordinate_values(self.cube, coord, values)
         self._check_fails_in_metadata()
 
-    def test_non_increasing_fix(self):
-        """Check automatic fix for direction."""
-        coord = self.cube.coord('latitude')
-        values = np.linspace(
-            coord.points[-1],
-            coord.points[0],
-            len(coord.points)
-        )
-        self._update_coordinate_values(self.cube, coord, values)
-        self._check_cube(automatic_fixes=True)
-        self._check_cube()
-        # test bounds are contiguous
-        bounds = self.cube.coord('latitude').bounds
-        right_bounds = bounds[:-2, 1]
-        left_bounds = bounds[1:-1, 0]
-        self.assertTrue(np.all(left_bounds == right_bounds))
-
     def test_non_decreasing(self):
         """Fail in metadata if decreasing coordinate is increasing."""
         self.var_info.coordinates['lat'].stored_direction = 'decreasing'
         self._check_fails_in_metadata()
 
-    def test_non_decreasing_fix(self):
-        """Check automatic fix for non decreasing coordinate."""
-        self.cube.data[0, 0, 0, 0, 0] = 70
+    # TODO: remove in v2.12
+    def test_non_decreasing_automatic_fix_metadata(self):
+        """Automatic fix for decreasing coordinate."""
         self.var_info.coordinates['lat'].stored_direction = 'decreasing'
-        self._check_cube(automatic_fixes=True)
-        self._check_cube()
-        index = [0, 0, 0, 0, 0]
-        index[self.cube.coord_dims('latitude')[0]] = -1
-        self.assertEqual(self.cube.data.item(tuple(index)), 70)
-        self.assertEqual(self.cube.data[0, 0, 0, 0, 0], 50)
-        cube_points = self.cube.coord('latitude').points
-        reference = np.linspace(90, -90, 20, endpoint=True)
-        for index in range(20):
-            np.testing.assert_allclose(cube_points[index], reference[index])
-        # test bounds are contiguous
-        bounds = self.cube.coord('latitude').bounds
-        right_bounds = bounds[:-2, 1]
-        left_bounds = bounds[1:-1, 0]
-        self.assertTrue(np.all(left_bounds == right_bounds))
+        checker = CMORCheck(self.cube, self.var_info, automatic_fixes=True)
+        checker.check_metadata()
 
-    def test_not_bounds(self):
-        """Warning if bounds are not available."""
-        self.cube.coord('longitude').bounds = None
-        self._check_warnings_on_metadata(automatic_fixes=False)
-        self.assertFalse(self.cube.coord('longitude').has_bounds())
+    # TODO: remove in v2.12
+    def test_non_decreasing_automatic_fix_data(self):
+        """Automatic fix for decreasing coordinate."""
+        self.var_info.coordinates['lat'].stored_direction = 'decreasing'
+        checker = CMORCheck(self.cube, self.var_info, automatic_fixes=True)
+        checker.check_data()
 
-    def test_guess_bounds_with_fixes(self):
-        """Warning if bounds added with automatic fixes."""
-        self.cube.coord('longitude').bounds = None
-        self._check_warnings_on_metadata(automatic_fixes=True)
-        self.assertTrue(self.cube.coord('longitude').has_bounds())
+    def test_lat_non_monotonic(self):
+        """Test fail for non monotonic latitude."""
+        lat = self.cube.coord('latitude')
+        points = np.array(lat.points)
+        points[-1] = points[0]
+        dims = self.cube.coord_dims(lat)
+        self.cube.remove_coord(lat)
+        lat = iris.coords.AuxCoord.from_coord(lat)
+        self.cube.add_aux_coord(lat.copy(points), dims)
+        self._check_fails_in_metadata()
 
-    def test_not_guess_bounds_with_fixes(self):
-        """Warning if bounds added with automatic fixes."""
+    def test_not_bounds(self):
+        """Warning if bounds are not available."""
         self.cube.coord('longitude').bounds = None
-        self.var_info.coordinates['lon'].must_have_bounds = "no"
-        self._check_cube(automatic_fixes=True)
+        self._check_warnings_on_metadata()
         self.assertFalse(self.cube.coord('longitude').has_bounds())
 
     def test_not_correct_lons(self):
@@ -794,34 +704,15 @@ class TestCMORCheck(unittest.TestCase):
         self.cube = self.cube.intersection(longitude=(-180., 180.))
         self._check_fails_in_metadata()
 
-    def test_lons_automatic_fix(self):
-        """Test automatic fixes for bad longitudes."""
-        self.cube = self.cube.intersection(longitude=(-180., 180.))
-        self._check_cube(automatic_fixes=True)
-
-    def test_lons_automatic_fix_with_bounds(self):
-        """Test automatic fixes for bad longitudes with added bounds."""
-        self.cube.coord('longitude').bounds = None
-        self.cube = self.cube.intersection(longitude=(-180., 180.))
-        self._check_cube(automatic_fixes=True)
-        self.assertTrue(self.cube.coord('longitude').points.min() >= 0.)
-        self.assertTrue(self.cube.coord('longitude').points.max() <= 360.)
-        self.assertTrue(self.cube.coord('longitude').has_bounds())
-
-    def test_high_lons_automatic_fix(self):
-        """Test automatic fixes for high longitudes."""
-        self.cube = self.cube.intersection(longitude=(180., 520.))
-        self._check_cube(automatic_fixes=True)
-
-    def test_too_high_lons_automatic_fix_not_done(self):
-        """Test automatic fixes for bad longitudes."""
+    def test_high_lons(self):
+        """Test bad longitudes."""
         self.cube = self.cube.intersection(longitude=(720., 1080.))
-        self._check_fails_in_metadata(automatic_fixes=True)
+        self._check_fails_in_metadata()
 
-    def test_too_low_lons_automatic_fix_not_done(self):
-        """Test automatic fixes for bad longitudes."""
+    def test_low_lons(self):
+        """Test bad longitudes."""
         self.cube = self.cube.intersection(longitude=(-720., -360.))
-        self._check_fails_in_metadata(automatic_fixes=True)
+        self._check_fails_in_metadata()
 
     def test_not_valid_min(self):
         """Fail if coordinate values below valid_min."""
@@ -862,49 +753,16 @@ class TestCMORCheck(unittest.TestCase):
         self.cube.coord('latitude').units = 'degrees_n'
         self._check_fails_in_metadata()
 
-    def test_units_automatic_fix(self):
-        """Test automatic fix for bad coordinate units."""
-        self.cube.coord('latitude').units = 'degrees_n'
-        self._check_cube(automatic_fixes=True)
-
-    def test_units_automatic_fix_failed(self):
-        """Test automatic fix fail for incompatible coordinate units."""
+    def test_non_convertible_units(self):
+        """Test fail for incompatible coordinate units."""
         self.cube.coord('latitude').units = 'degC'
-        self._check_fails_in_metadata(automatic_fixes=True)
+        self._check_fails_in_metadata()
 
     def test_bad_time(self):
         """Fail if time have bad units."""
         self.cube.coord('time').units = 'days'
         self._check_fails_in_metadata()
 
-    def test_time_units_automatic_fix(self):
-        """Test automatic fix for time units."""
-        self.cube.coord('time').units = 'days since 1860-1-1 00:00:00'
-        self.cube.attributes['parent_time_units'] = 'days since ' \
-                                                    '1850-1-1 00:00:00'
-        self.cube.attributes['branch_time_in_parent'] = 0.
-        self.cube.attributes['branch_time_in_child'] = 0.
-        self._check_cube()
-        assert (self.cube.coord('time').units.origin ==
-                'days since 1850-1-1 00:00:00')
-        assert self.cube.attributes['parent_time_units'] == 'days since ' \
-                                                            '1850-1-1 00:00:00'
-        assert self.cube.attributes['branch_time_in_parent'] == 0.
-        assert self.cube.attributes['branch_time_in_child'] == 3652.
-
-    def test_time_units_no_parent_exp(self):
-        """Test non-conversion of time units attributes for no parent exps."""
-        self.cube.coord('time').units = 'days since 1860-1-1 00:00:00'
-        self.cube.attributes['parent_time_units'] = 'no parent'
-        self.cube.attributes['branch_time_in_parent'] = 0.
-        self.cube.attributes['branch_time_in_child'] = 0.
-        self._check_cube()
-        assert (self.cube.coord('time').units.origin ==
-                'days since 1850-1-1 00:00:00')
-        assert self.cube.attributes['parent_time_units'] == 'no parent'
-        assert self.cube.attributes['branch_time_in_parent'] == 0.
-        assert self.cube.attributes['branch_time_in_child'] == 0.
-
     def test_wrong_parent_time_unit(self):
         """Test fail for wrong parent time units."""
         self.cube.coord('time').units = 'days since 1860-1-1 00:00:00'
@@ -916,13 +774,13 @@ class TestCMORCheck(unittest.TestCase):
         assert self.cube.attributes['branch_time_in_parent'] == 0.
         assert self.cube.attributes['branch_time_in_child'] == 0
 
-    def test_time_automatic_fix_failed(self):
-        """Test automatic fix fail for incompatible time units."""
+    def test_time_non_time_units(self):
+        """Test fail for incompatible time units."""
         self.cube.coord('time').units = 'K'
-        self._check_fails_in_metadata(automatic_fixes=True)
+        self._check_fails_in_metadata()
 
     def test_time_non_monotonic(self):
-        """Test automatic fix fail for non monotonic times."""
+        """Test fail for non monotonic times."""
         time = self.cube.coord('time')
         points = np.array(time.points)
         points[-1] = points[0]
@@ -930,61 +788,13 @@ class TestCMORCheck(unittest.TestCase):
         self.cube.remove_coord(time)
         time = iris.coords.AuxCoord.from_coord(time)
         self.cube.add_aux_coord(time.copy(points), dims)
-        self._check_fails_in_metadata(automatic_fixes=True)
+        self._check_fails_in_metadata()
 
     def test_bad_standard_name(self):
         """Fail if coordinates have bad standard names at metadata step."""
         self.cube.coord('time').standard_name = 'region'
         self._check_fails_in_metadata()
 
-    def test_bad_out_name_multidim_latitude(self):
-        """Warning if multidimensional lat has bad var_name at metadata"""
-        self.var_info.table_type = 'CMIP6'
-        self.cube.remove_coord('latitude')
-        self.cube.add_aux_coord(
-            iris.coords.AuxCoord(
-                np.reshape(np.linspace(-90, 90, num=20*20), (20, 20)),
-                var_name='bad_name',
-                standard_name='latitude',
-                units='degrees_north'
-            ),
-            (1, 2)
-        )
-        self._check_debug_messages_on_metadata()
-
-    def test_bad_out_name_multidim_longitude(self):
-        """Warning if multidimensional lon has bad var_name at metadata"""
-        self.var_info.table_type = 'CMIP6'
-        self.cube.remove_coord('longitude')
-        self.cube.add_aux_coord(
-            iris.coords.AuxCoord(
-                np.reshape(np.linspace(-90, 90, num=20*20), (20, 20)),
-                var_name='bad_name',
-                standard_name='longitude',
-                units='degrees_east'
-            ),
-            (1, 2)
-        )
-        self._check_debug_messages_on_metadata(automatic_fixes=True)
-
-    def test_bad_bounds_in_multidim_longitude(self):
-        """Warning if multidimensional lon has bad var_name at metadata"""
-        self.var_info.table_type = 'CMIP6'
-        self.cube.remove_coord('longitude')
-        lons = np.reshape(np.linspace(180, 540, num=20*20), (20, 20))
-        bounds = np.stack([lons.copy() - 0.5, lons.copy() + 0.5], -1)
-        self.cube.add_aux_coord(
-            iris.coords.AuxCoord(
-                lons,
-                var_name='bad_name',
-                standard_name='longitude',
-                units='degrees_east',
-                bounds=bounds,
-            ),
-            (1, 2)
-        )
-        self._check_debug_messages_on_metadata(automatic_fixes=True)
-
     def test_bad_out_name_region_area_type(self):
         """Debug message if region/area_type AuxCoord has bad var_name at
         metadata."""
@@ -1086,49 +896,20 @@ class TestCMORCheck(unittest.TestCase):
         """Fail at metadata if frequency is not supported."""
         self._check_fails_in_metadata(frequency='wrong_freq')
 
-    def test_time_bounds(self):
-        """Test time bounds are guessed for all frequencies"""
-        freqs = ['hr', '3hr', '6hr', 'day', 'mon', 'yr', 'dec']
-        guessed = []
-        for freq in freqs:
-            cube = self.get_cube(self.var_info, frequency=freq)
-            cube.coord('time').bounds = None
-            self.cube = cube
-            self._check_cube(automatic_fixes=True, frequency=freq)
-            if cube.coord('time').bounds is not None:
-                guessed.append(True)
-        assert len(guessed) == len(freqs)
-
-    def test_no_time_bounds(self):
-        """Test time bounds are not guessed for instantaneous data"""
-        self.var_info.coordinates['time'].must_have_bounds = 'no'
-        self.var_info.coordinates['time1'] = (
-            self.var_info.coordinates.pop('time'))
-        self.cube.coord('time').bounds = None
-        self._check_cube(automatic_fixes=True)
-        guessed_bounds = self.cube.coord('time').bounds
-        assert guessed_bounds is None
-
     def test_hr_mip_cordex(self):
         """Test hourly CORDEX tables are found."""
         checker = _get_cmor_checker('CORDEX', '3hr', 'tas', '3hr')
         assert checker(self.cube)._cmor_var.short_name == 'tas'
         assert checker(self.cube)._cmor_var.frequency == '3hr'
 
-    def test_set_range_in_0_360(self):
-        checker = _get_cmor_checker('CMIP5', 'Amon', 'tas', 'mon')
-        arr_in = np.array([[-120.0, 0.0, 20.0], [-1.0, 360.0, 400.0]])
-        arr_exp = np.array([[240.0, 0.0, 20.0], [359.0, 0.0, 40.0]])
-        arr_out = checker(self.cube)._set_range_in_0_360(arr_in)
-        np.testing.assert_allclose(arr_out, arr_exp)
-
-    def test_set_range_in_0_360_lazy(self):
-        checker = _get_cmor_checker('CMIP5', 'Amon', 'tas', 'mon')
-        arr_in = da.from_array([[-120.0, 0.0, 20.0], [-1.0, 360.0, 400.0]])
-        arr_exp = da.from_array([[240.0, 0.0, 20.0], [359.0, 0.0, 40.0]])
-        arr_out = checker(self.cube)._set_range_in_0_360(arr_in)
-        self.assertTrue(isinstance(arr_out, da.core.Array))
-        np.testing.assert_allclose(arr_out.compute(), arr_exp.compute())
+    def test_custom_variable(self):
+        checker = _get_cmor_checker('OBS', 'Amon', 'uajet', 'mon')
+        assert checker(self.cube)._cmor_var.short_name == 'uajet'
+        assert checker(self.cube)._cmor_var.long_name == (
+            'Jet position expressed as latitude of maximum meridional wind '
+            'speed'
+        )
+        assert checker(self.cube)._cmor_var.units == 'degrees'
 
     def _check_fails_on_data(self):
         checker = CMORCheck(self.cube, self.var_info)
@@ -1210,7 +991,7 @@ class TestCMORCheck(unittest.TestCase):
 
         return cube
 
-    def _get_unstructed_grid_cube(self, correct_lon=True, n_bounds=2):
+    def _get_unstructed_grid_cube(self, n_bounds=2):
         """Get cube with unstructured grid."""
         assert n_bounds in (2, 3), "Only 2 or 3 bounds per cell supported"
 
@@ -1256,23 +1037,6 @@ class TestCMORCheck(unittest.TestCase):
                 ))
                 coord.bounds = np.swapaxes(new_bounds, 0, 1)
 
-        # Convert longitude to [-180, 180] to check automatic fixes if desired
-        if correct_lon:
-            return cube
-        new_lon = iris.coords.AuxCoord(
-            points=cube.coord('longitude').points,
-            bounds=cube.coord('longitude').bounds,
-            var_name='lon',
-            standard_name='longitude',
-            long_name='Longitude',
-            units='degrees_east',
-        )
-        new_lon.points = np.where(new_lon.points < 180.0, new_lon.points,
-                                  new_lon.points - 360.0)
-        new_lon.bounds = np.where(new_lon.bounds < 180.0, new_lon.bounds,
-                                  new_lon.bounds - 360.0)
-        cube.remove_coord('longitude')
-        cube.add_aux_coord(new_lon, 1)
         return cube
 
     def _setup_generic_level_var(self):
@@ -1450,5 +1214,17 @@ class TestCMORCheck(unittest.TestCase):
         return np.arange(start, end, step=delta)
 
 
+def test_get_cmor_checker_invalid_project_fail():
+    """Test ``_get_cmor_checker`` with invalid project."""
+    with pytest.raises(KeyError):
+        _get_cmor_checker('INVALID_PROJECT', 'mip', 'short_name', 'frequency')
+
+
+def test_deprecate_automatic_fixes():
+    """Test deprecation of automatic_fixes."""
+    with pytest.warns(ESMValCoreDeprecationWarning):
+        CMORCheck('cube', 'var_info', 'frequency', automatic_fixes=True)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/unit/cmor/test_fix.py b/tests/unit/cmor/test_fix.py
index 86293e2aa..d6a06b2c1 100644
--- a/tests/unit/cmor/test_fix.py
+++ b/tests/unit/cmor/test_fix.py
@@ -1,16 +1,17 @@
 """Unit tests for :mod:`esmvalcore.cmor.fix`."""
 
 from pathlib import Path
-from unittest import TestCase
 from unittest.mock import Mock, patch, sentinel
 
-from esmvalcore.cmor.check import CheckLevels
+import pytest
+
 from esmvalcore.cmor.fix import Fix, fix_data, fix_file, fix_metadata
 
 
-class TestFixFile(TestCase):
+class TestFixFile():
     """Fix file tests."""
 
+    @pytest.fixture(autouse=True)
     def setUp(self):
         """Prepare for testing."""
         self.filename = 'filename'
@@ -26,8 +27,10 @@ class TestFixFile(TestCase):
                 'dataset': 'model',
                 'mip': 'mip',
                 'short_name': 'short_name',
+                'frequency': 'frequency',
             },
             'session': sentinel.session,
+            'frequency': 'frequency',
         }
 
     def test_fix(self):
@@ -42,9 +45,10 @@ class TestFixFile(TestCase):
                 mip='mip',
                 output_dir=Path('output_dir'),
                 session=sentinel.session,
+                frequency='frequency',
             )
-            self.assertNotEqual(file_returned, self.filename)
-            self.assertEqual(file_returned, 'new_filename')
+            assert file_returned != self.filename
+            assert file_returned == 'new_filename'
             mock_get_fixes.assert_called_once_with(
                 **self.expected_get_fixes_call
             )
@@ -61,16 +65,18 @@ class TestFixFile(TestCase):
                 mip='mip',
                 output_dir=Path('output_dir'),
                 session=sentinel.session,
+                frequency='frequency',
             )
-            self.assertEqual(file_returned, self.filename)
+            assert file_returned == self.filename
             mock_get_fixes.assert_called_once_with(
                 **self.expected_get_fixes_call
             )
 
 
-class TestGetCube(TestCase):
+class TestGetCube():
     """Test get cube by var_name method."""
 
+    @pytest.fixture(autouse=True)
     def setUp(self):
         """Prepare for testing."""
         self.cube_1 = Mock()
@@ -84,28 +90,27 @@ class TestGetCube(TestCase):
 
     def test_get_first_cube(self):
         """Test selecting first cube."""
-        self.assertIs(self.cube_1,
-                      self.fix.get_cube_from_list(self.cubes, "cube1"))
+        assert self.cube_1 is self.fix.get_cube_from_list(self.cubes, "cube1")
 
     def test_get_second_cube(self):
         """Test selecting second cube."""
-        self.assertIs(self.cube_2,
-                      self.fix.get_cube_from_list(self.cubes, "cube2"))
+        assert self.cube_2 is self.fix.get_cube_from_list(self.cubes, "cube2")
 
     def test_get_default_raises(self):
         """Check that the default raises (Fix is not a cube)."""
-        with self.assertRaises(Exception):
+        with pytest.raises(Exception):
             self.fix.get_cube_from_list(self.cubes)
 
     def test_get_default(self):
         """Check that the default return the cube (fix is a cube)."""
         self.cube_1.var_name = 'fix'
-        self.assertIs(self.cube_1, self.fix.get_cube_from_list(self.cubes))
+        assert self.cube_1 is self.fix.get_cube_from_list(self.cubes)
 
 
-class TestFixMetadata(TestCase):
+class TestFixMetadata():
     """Fix metadata tests."""
 
+    @pytest.fixture(autouse=True)
     def setUp(self):
         """Prepare for testing."""
         self.cube = self._create_mock_cube()
@@ -125,9 +130,10 @@ class TestFixMetadata(TestCase):
                 'dataset': 'model',
                 'mip': 'mip',
                 'short_name': 'short_name',
-                'frequency': None,
+                'frequency': 'frequency',
             },
             'session': sentinel.session,
+            'frequency': 'frequency',
         }
 
     @staticmethod
@@ -150,9 +156,12 @@ class TestFixMetadata(TestCase):
                     project='project',
                     dataset='model',
                     mip='mip',
+                    frequency='frequency',
                     session=sentinel.session,
                 )[0]
-                self.checker.assert_called_once_with(self.intermediate_cube)
+                self.checker.assert_called_once_with(
+                    self.intermediate_cube
+                )
                 self.check_metadata.assert_called_once_with()
                 assert cube_returned is not self.cube
                 assert cube_returned is not self.intermediate_cube
@@ -174,6 +183,7 @@ class TestFixMetadata(TestCase):
                     project='project',
                     dataset='model',
                     mip='mip',
+                    frequency='frequency',
                     session=sentinel.session,
                 )[0]
                 self.checker.assert_called_once_with(self.cube)
@@ -193,8 +203,7 @@ class TestFixMetadata(TestCase):
             with patch('esmvalcore.cmor.fix._get_cmor_checker',
                        return_value=self.checker):
                 cube_returned = fix_metadata(
-                    cubes=[self.cube,
-                           self._create_mock_cube('extra')],
+                    cubes=[self.cube, self._create_mock_cube('extra')],
                     short_name='short_name',
                     project='CMIP6',
                     dataset='model',
@@ -205,52 +214,25 @@ class TestFixMetadata(TestCase):
                 assert cube_returned is self.cube
 
     def test_select_var_failed_if_bad_var_name(self):
-        """Check that the same cube is returned if no fix is available."""
-        with patch('esmvalcore.cmor._fixes.fix.Fix.get_fixes',
-                   return_value=[]):
-            with self.assertRaises(ValueError):
-                fix_metadata(
-                    cubes=[
-                        self._create_mock_cube('not_me'),
-                        self._create_mock_cube('me_neither')
-                    ],
-                    short_name='short_name',
-                    project='CMIP6',
-                    dataset='model',
-                    mip='mip',
-                )
-
-    def test_cmor_checker_called(self):
-        """Check that the cmor check is done."""
-        checker = Mock()
-        checker.return_value = Mock()
-        with patch('esmvalcore.cmor._fixes.fix.Fix.get_fixes',
-                   return_value=[]):
-            with patch('esmvalcore.cmor.fix._get_cmor_checker',
-                       return_value=checker) as get_mock:
-                fix_metadata(
-                    cubes=[self.cube],
-                    short_name='short_name',
-                    project='CMIP6',
-                    dataset='dataset',
-                    mip='mip',
-                    frequency='frequency',
-                )
-                get_mock.assert_called_once_with(
-                    automatic_fixes=True,
-                    fail_on_error=False,
-                    frequency='frequency',
-                    mip='mip',
-                    short_name='short_name',
-                    table='CMIP6',
-                    check_level=CheckLevels.DEFAULT,)
-                checker.assert_called_once_with(self.cube)
-                checker.return_value.check_metadata.assert_called_once_with()
+        """Check that an error is raised if short_names do not match."""
+        msg = "More than one cube found for variable tas in CMIP6:model"
+        with pytest.raises(ValueError, match=msg):
+            fix_metadata(
+                cubes=[
+                    self._create_mock_cube('not_me'),
+                    self._create_mock_cube('me_neither')
+                ],
+                short_name='tas',
+                project='CMIP6',
+                dataset='model',
+                mip='Amon',
+            )
 
 
-class TestFixData(TestCase):
+class TestFixData():
     """Fix data tests."""
 
+    @pytest.fixture(autouse=True)
     def setUp(self):
         """Prepare for testing."""
         self.cube = Mock()
@@ -270,9 +252,10 @@ class TestFixData(TestCase):
                 'dataset': 'model',
                 'mip': 'mip',
                 'short_name': 'short_name',
-                'frequency': None,
+                'frequency': 'frequency',
             },
             'session': sentinel.session,
+            'frequency': 'frequency',
         }
 
     def test_fix(self):
@@ -288,9 +271,12 @@ class TestFixData(TestCase):
                     project='project',
                     dataset='model',
                     mip='mip',
+                    frequency='frequency',
                     session=sentinel.session,
                 )
-                self.checker.assert_called_once_with(self.intermediate_cube)
+                self.checker.assert_called_once_with(
+                    self.intermediate_cube
+                )
                 self.check_data.assert_called_once_with()
                 assert cube_returned is not self.cube
                 assert cube_returned is not self.intermediate_cube
@@ -312,6 +298,7 @@ class TestFixData(TestCase):
                     project='project',
                     dataset='model',
                     mip='mip',
+                    frequency='frequency',
                     session=sentinel.session,
                 )
                 self.checker.assert_called_once_with(self.cube)
@@ -322,25 +309,3 @@ class TestFixData(TestCase):
                 mock_get_fixes.assert_called_once_with(
                     **self.expected_get_fixes_call
                 )
-
-    def test_cmor_checker_called(self):
-        """Check that the cmor check is done."""
-        checker = Mock()
-        checker.return_value = Mock()
-        with patch('esmvalcore.cmor._fixes.fix.Fix.get_fixes',
-                   return_value=[]):
-            with patch('esmvalcore.cmor.fix._get_cmor_checker',
-                       return_value=checker) as get_mock:
-                fix_data(self.cube, 'short_name', 'CMIP6', 'model', 'mip',
-                         'frequency')
-                get_mock.assert_called_once_with(
-                    table='CMIP6',
-                    automatic_fixes=True,
-                    check_level=CheckLevels.DEFAULT,
-                    fail_on_error=False,
-                    frequency='frequency',
-                    mip='mip',
-                    short_name='short_name',
-                )
-                checker.assert_called_once_with(self.cube)
-                checker.return_value.check_data.assert_called_once_with()
diff --git a/tests/unit/cmor/test_generic_fix.py b/tests/unit/cmor/test_generic_fix.py
new file mode 100644
index 000000000..54703b93f
--- /dev/null
+++ b/tests/unit/cmor/test_generic_fix.py
@@ -0,0 +1,237 @@
+"""Unit tests for generic fixes."""
+
+from unittest.mock import sentinel
+
+import numpy as np
+import pytest
+from iris.coords import AuxCoord
+from iris.cube import Cube, CubeList
+
+from esmvalcore.cmor._fixes.fix import GenericFix, get_time_bounds
+from esmvalcore.cmor.table import get_var_info
+
+
+@pytest.fixture
+def time_coord():
+    """Time coordinate."""
+    time_coord = AuxCoord(
+        [15, 350],
+        standard_name='time',
+        units='days since 1850-01-01'
+    )
+    return time_coord
+
+
+@pytest.fixture
+def generic_fix():
+    """Generic fix object."""
+    vardef = get_var_info('CMIP6', 'CFmon', 'ta')
+    extra_facets = {'short_name': 'ta', 'project': 'CMIP6', 'dataset': 'MODEL'}
+    return GenericFix(vardef, extra_facets=extra_facets)
+
+
+@pytest.mark.parametrize(
+    'freq,expected_bounds',
+    [
+        ('mon', [[0, 31], [334, 365]]),
+        ('mo', [[0, 31], [334, 365]]),
+        ('yr', [[0, 365], [0, 365]]),
+        ('dec', [[0, 3652], [0, 3652]]),
+        ('day', [[14.5, 15.5], [349.5, 350.5]]),
+        ('6hr', [[14.875, 15.125], [349.875, 350.125]]),
+        ('3hr', [[14.9375, 15.0625], [349.9375, 350.0625]]),
+        ('1hr', [[14.97916666, 15.020833333], [349.97916666, 350.020833333]]),
+    ]
+)
+def test_get_time_bounds(time_coord, freq, expected_bounds):
+    """Test ``get_time_bounds`."""
+    bounds = get_time_bounds(time_coord, freq)
+    np.testing.assert_allclose(bounds, expected_bounds)
+
+
+def test_get_time_bounds_invalid_freq_fail(time_coord):
+    """Test ``get_time_bounds`."""
+    with pytest.raises(NotImplementedError):
+        get_time_bounds(time_coord, 'invalid_freq')
+
+
+def test_generic_fix_empty_long_name(generic_fix, monkeypatch):
+    """Test ``GenericFix``."""
+    # Artificially set long_name to empty string for test
+    monkeypatch.setattr(generic_fix.vardef, 'long_name', '')
+
+    cube = generic_fix._fix_long_name(sentinel.cube)
+
+    assert cube == sentinel.cube
+
+
+def test_generic_fix_empty_units(generic_fix, monkeypatch):
+    """Test ``GenericFix``."""
+    # Artificially set latitude units to empty string for test
+    coord_info = generic_fix.vardef.coordinates['latitude']
+    monkeypatch.setattr(coord_info, 'units', '')
+
+    ret = generic_fix._fix_coord_units(
+        sentinel.cube, coord_info, sentinel.cube_coord
+    )
+
+    assert ret is None
+
+
+def test_generic_fix_no_generic_lev_coords(generic_fix, monkeypatch):
+    """Test ``GenericFix``."""
+    # Artificially remove generic_lev_coords
+    monkeypatch.setattr(
+        generic_fix.vardef.coordinates['alevel'], 'generic_lev_coords', {}
+    )
+
+    cube = generic_fix._fix_alternative_generic_level_coords(sentinel.cube)
+
+    assert cube == sentinel.cube
+
+
+def test_requested_levels_2d_coord(generic_fix, mocker):
+    """Test ``GenericFix``."""
+    cube_coord = AuxCoord([[0]], standard_name='latitude', units='rad')
+    cmor_coord = mocker.Mock(requested=True)
+
+    ret = generic_fix._fix_requested_coord_values(
+        sentinel.cube, cmor_coord, cube_coord
+    )
+
+    assert ret is None
+
+
+def test_requested_levels_invalid_arr(generic_fix, mocker):
+    """Test ``GenericFix``."""
+    cube_coord = AuxCoord([0], standard_name='latitude', units='rad')
+    cmor_coord = mocker.Mock(requested=['a', 'b'])
+
+    ret = generic_fix._fix_requested_coord_values(
+        sentinel.cube, cmor_coord, cube_coord
+    )
+
+    assert ret is None
+
+
+def test_lon_no_fix_needed(generic_fix):
+    """Test ``GenericFix``."""
+    cube_coord = AuxCoord(
+        [0.0, 180.0, 360.0], standard_name='longitude', units='rad'
+    )
+
+    ret = generic_fix._fix_longitude_0_360(
+        sentinel.cube, sentinel.cmor_coord, cube_coord
+    )
+
+    assert ret == (sentinel.cube, cube_coord)
+
+
+def test_lon_too_low_to_fix(generic_fix):
+    """Test ``GenericFix``."""
+    cube_coord = AuxCoord(
+        [-370.0, 0.0], standard_name='longitude', units='rad'
+    )
+
+    ret = generic_fix._fix_longitude_0_360(
+        sentinel.cube, sentinel.cmor_coord, cube_coord
+    )
+
+    assert ret == (sentinel.cube, cube_coord)
+
+
+def test_lon_too_high_to_fix(generic_fix):
+    """Test ``GenericFix``."""
+    cube_coord = AuxCoord([750.0, 0.0], standard_name='longitude', units='rad')
+
+    ret = generic_fix._fix_longitude_0_360(
+        sentinel.cube, sentinel.cmor_coord, cube_coord
+    )
+
+    assert ret == (sentinel.cube, cube_coord)
+
+
+def test_fix_direction_2d_coord(generic_fix):
+    """Test ``GenericFix``."""
+    cube_coord = AuxCoord([[0]], standard_name='latitude', units='rad')
+
+    ret = generic_fix._fix_coord_direction(
+        sentinel.cube, sentinel.cmor_coord, cube_coord
+    )
+
+    assert ret == (sentinel.cube, cube_coord)
+
+
+def test_fix_direction_string_coord(generic_fix):
+    """Test ``GenericFix``."""
+    cube_coord = AuxCoord(['a'], standard_name='latitude', units='rad')
+
+    ret = generic_fix._fix_coord_direction(
+        sentinel.cube, sentinel.cmor_coord, cube_coord
+    )
+
+    assert ret == (sentinel.cube, cube_coord)
+
+
+def test_fix_direction_no_stored_direction(generic_fix, mocker):
+    """Test ``GenericFix``."""
+    cube = Cube(0)
+    cube_coord = AuxCoord([0, 1], standard_name='latitude', units='rad')
+    cmor_coord = mocker.Mock(stored_direction='')
+
+    ret = generic_fix._fix_coord_direction(cube, cmor_coord, cube_coord)
+
+    assert ret == (cube, cube_coord)
+
+
+def test_fix_metadata_not_fail_with_empty_cube(generic_fix):
+    """Generic fixes should not fail with empty cubes."""
+    fixed_cubes = generic_fix.fix_metadata([Cube(0)])
+
+    assert isinstance(fixed_cubes, CubeList)
+    assert len(fixed_cubes) == 1
+    assert fixed_cubes[0] == Cube(
+        0, standard_name='air_temperature', long_name='Air Temperature'
+    )
+
+
+@pytest.mark.parametrize(
+    'extra_facets', [{}, {'project': 'P', 'dataset': 'D'}]
+)
+def test_fix_metadata_multiple_cubes_fail(extra_facets):
+    """Generic fixes should fail when multiple invalid cubes are given."""
+    vardef = get_var_info('CMIP6', 'Amon', 'ta')
+    fix = GenericFix(vardef, extra_facets=extra_facets)
+    with pytest.raises(ValueError):
+        fix.fix_metadata([Cube(0), Cube(0)])
+
+
+def test_fix_metadata_no_extra_facets():
+    """Generic fixes should not fail when no extra facets are given."""
+    vardef = get_var_info('CMIP6', 'Amon', 'ta')
+    fix = GenericFix(vardef)
+    fixed_cubes = fix.fix_metadata([Cube(0)])
+
+    assert isinstance(fixed_cubes, CubeList)
+    assert len(fixed_cubes) == 1
+    assert fixed_cubes[0] == Cube(
+        0, standard_name='air_temperature', long_name='Air Temperature'
+    )
+
+
+def test_fix_data_not_fail_with_empty_cube(generic_fix):
+    """Generic fixes should not fail with empty cubes."""
+    fixed_cube = generic_fix.fix_data(Cube(0))
+
+    assert isinstance(fixed_cube, Cube)
+    assert fixed_cube == Cube(0)
+
+
+def test_fix_data_no_extra_facets():
+    """Generic fixes should not fail when no extra facets are given."""
+    vardef = get_var_info('CMIP6', 'Amon', 'ta')
+    fix = GenericFix(vardef)
+    fixed_cube = fix.fix_data(Cube(0))
+
+    assert isinstance(fixed_cube, Cube)
+    assert fixed_cube == Cube(0)
diff --git a/tests/unit/cmor/test_utils.py b/tests/unit/cmor/test_utils.py
new file mode 100644
index 000000000..f373524a7
--- /dev/null
+++ b/tests/unit/cmor/test_utils.py
@@ -0,0 +1,60 @@
+"""Unit tests for :mod:`esmvalcore.cmor._utils`."""
+
+import pytest
+from iris.cube import Cube
+
+from esmvalcore.cmor._utils import _get_single_cube
+
+
+@pytest.mark.parametrize(
+    'cubes', [[Cube(0)], [Cube(0, var_name='x')], [Cube(0, var_name='y')]]
+)
+def test_get_single_cube_one_cube(cubes, caplog):
+    """Test ``_get_single_cube``."""
+    single_cube = _get_single_cube(cubes, 'x')
+    assert single_cube == cubes[0]
+    assert not caplog.records
+
+
+@pytest.mark.parametrize(
+    'dataset_str,msg', [
+        (None, "Found variable x, but"),
+        ('XYZ', "Found variable x in XYZ, but"),
+    ]
+)
+@pytest.mark.parametrize(
+    'cubes', [
+        [Cube(0), Cube(0, var_name='x')],
+        [Cube(0, var_name='x'), Cube(0)],
+        [Cube(0, var_name='x'), Cube(0, var_name='x')],
+        [Cube(0), Cube(0), Cube(0, var_name='x')],
+    ]
+)
+def test_get_single_cube_multiple_cubes(cubes, dataset_str, msg, caplog):
+    """Test ``_get_single_cube``."""
+    single_cube = _get_single_cube(cubes, 'x', dataset_str=dataset_str)
+    assert single_cube == Cube(0, var_name='x')
+    assert len(caplog.records) == 1
+    log = caplog.records[0]
+    assert log.levelname == 'WARNING'
+    assert msg in log.message
+
+
+@pytest.mark.parametrize(
+    'dataset_str,msg', [
+        (None, "More than one cube found for variable x but"),
+        ('XYZ', "More than one cube found for variable x in XYZ but"),
+    ]
+)
+@pytest.mark.parametrize(
+    'cubes', [
+        [Cube(0), Cube(0)],
+        [Cube(0, var_name='y'), Cube(0)],
+        [Cube(0, var_name='y'), Cube(0, var_name='z')],
+        [Cube(0), Cube(0), Cube(0, var_name='z')],
+    ]
+)
+def test_get_single_cube_no_cubes_fail(cubes, dataset_str, msg):
+    """Test ``_get_single_cube``."""
+    with pytest.raises(ValueError, match=msg):
+        _get_single_cube(cubes, 'x', dataset_str=dataset_str)
diff --git a/tests/unit/test_cmor_api.py b/tests/unit/test_cmor_api.py
index 781ce3015..ac61c5006 100644
--- a/tests/unit/test_cmor_api.py
+++ b/tests/unit/test_cmor_api.py
@@ -1,4 +1,6 @@
 # flake8: noqa
+from unittest.mock import sentinel
+
 import esmvalcore.cmor.check
 import esmvalcore.cmor.fix
 import esmvalcore.cmor.fixes
@@ -11,3 +13,114 @@ from esmvalcore.cmor.check import (
     cmor_check_data,
     cmor_check_metadata,
 )
+
+
+def test_cmor_check_metadata(mocker):
+    """Test ``cmor_check_metadata``"""
+    mock_get_cmor_checker = mocker.patch.object(
+        esmvalcore.cmor.check, '_get_cmor_checker', autospec=True
+    )
+    (
+        mock_get_cmor_checker.return_value.return_value.check_metadata.
+        return_value
+    ) = sentinel.checked_cube
+
+    cube = cmor_check_metadata(
+        sentinel.cube,
+        sentinel.cmor_table,
+        sentinel.mip,
+        sentinel.short_name,
+        sentinel.frequency,
+        check_level=sentinel.check_level,
+    )
+
+    mock_get_cmor_checker.assert_called_once_with(
+        sentinel.cmor_table,
+        sentinel.mip,
+        sentinel.short_name,
+        sentinel.frequency,
+        check_level=sentinel.check_level,
+    )
+    mock_get_cmor_checker.return_value.assert_called_once_with(sentinel.cube)
+    (
+        mock_get_cmor_checker.return_value.return_value.check_metadata.
+        assert_called_once_with()
+    )
+    assert cube == sentinel.checked_cube
+
+
+def test_cmor_check_data(mocker):
+    """Test ``cmor_check_data``"""
+    mock_get_cmor_checker = mocker.patch.object(
+        esmvalcore.cmor.check, '_get_cmor_checker', autospec=True
+    )
+    (
+        mock_get_cmor_checker.return_value.return_value.check_data.
+        return_value
+    ) = sentinel.checked_cube
+
+    cube = cmor_check_data(
+        sentinel.cube,
+        sentinel.cmor_table,
+        sentinel.mip,
+        sentinel.short_name,
+        sentinel.frequency,
+        check_level=sentinel.check_level,
+    )
+
+    mock_get_cmor_checker.assert_called_once_with(
+        sentinel.cmor_table,
+        sentinel.mip,
+        sentinel.short_name,
+        sentinel.frequency,
+        check_level=sentinel.check_level,
+    )
+    mock_get_cmor_checker.return_value.assert_called_once_with(sentinel.cube)
+    (
+        mock_get_cmor_checker.return_value.return_value.check_data.
+        assert_called_once_with()
+    )
+    assert cube == sentinel.checked_cube
+
+
+def test_cmor_check(mocker):
+    """Test ``cmor_check``"""
+    mock_cmor_check_metadata = mocker.patch.object(
+        esmvalcore.cmor.check,
+        'cmor_check_metadata',
+        autospec=True,
+        return_value=sentinel.cube_after_check_metadata,
+    )
+    mock_cmor_check_data = mocker.patch.object(
+        esmvalcore.cmor.check,
+        'cmor_check_data',
+        autospec=True,
+        return_value=sentinel.cube_after_check_data,
+    )
+
+    cube = cmor_check(
+        sentinel.cube,
+        sentinel.cmor_table,
+        sentinel.mip,
+        sentinel.short_name,
+        sentinel.frequency,
+        sentinel.check_level,
+    )
+
+    mock_cmor_check_metadata.assert_called_once_with(
+        sentinel.cube,
+        sentinel.cmor_table,
+        sentinel.mip,
+        sentinel.short_name,
+        sentinel.frequency,
+        check_level=sentinel.check_level,
+    )
+    mock_cmor_check_data.assert_called_once_with(
+        sentinel.cube_after_check_metadata,
+        sentinel.cmor_table,
+        sentinel.mip,
+        sentinel.short_name,
+        sentinel.frequency,
+        check_level=sentinel.check_level,
+    )
+    assert cube == sentinel.cube_after_check_data