def METHOD_NAME(self, rng, fields, dfields, epsilon_approx, epsilon_assert):
    fields = g.util.to_list(fields)
    dfields = g.util.to_list(dfields)
    weights = rng.normal_element(g.group.cartesian(dfields))
    # the functional needs to be real
    eps = complex(self(fields)).imag
    g.message(f"Test that functional is real: {eps}")
    assert eps == 0.0
    # the gradient needs to be correct
    gradient = self.gradient(fields, dfields)
    a = sum([g.group.inner_product(w, gr) for gr, w in zip(gradient, weights)])
    b = self.approximate_gradient(fields, dfields, weights, epsilon=epsilon_approx)
    eps = abs(a - b) / abs(b)
    g.message(f"Assert gradient error: {eps} < {epsilon_assert}")
    if eps > epsilon_assert:
        g.message(f"Error: gradient = {a} <> approximate_gradient = {b}")
        assert False
    # the gradient needs to live in cartesian
    for gr, ww in zip(gradient, weights):
        if gr.otype.__name__ != ww.otype.__name__:
            g.message(
                f"Gradient has incorrect object type: {gr.otype.__name__} != {ww.otype.__name__}"
            )
        eps = g.group.defect(gr)
        if eps > epsilon_assert:
            g.message(f"Error: cartesian defect: {eps} > {epsilon_assert}")
            assert False