dbt-core icon indicating copy to clipboard operation
dbt-core copied to clipboard

[Bug] Unit test raising error `'<macro name>' is undefined. This can happen when calling a macro that does not exist.`

Open dbeatty10 opened this issue 1 year ago • 8 comments

Is this a new bug in dbt-core?

  • [X] I believe this is a new bug in dbt-core
  • [X] I have searched the existing issues, and I could not find an existing issue for this bug

Current Behavior

Sometimes (but not always!), I get this compilation error when a model contains a macro:

13:04:08    Compilation Error in unit_test test__model_f (models/unit_test.yml)
  'british_colours' is undefined. This can happen when calling a macro that does not exist. Check for typos and/or install package dependencies with "dbt deps".

Expected Behavior

This seems like it should work without needing to add an override for any macros.

Steps To Reproduce

macros/my_macros.sql

{% macro british_colours() -%}
    {{ return(["grey"]) }}
{%- endmacro %}


{% macro american_colors() -%}
    {{ return(["gray"]) }}
{%- endmacro %}

models/_unit_tests.yml

unit_tests:
  - name: test__model_f
    model: model_f
    given: []
    expect:
      rows:
        - {id: 1}

models/model_f.sql

{% set a_values = british_colours() %}
{% set b_values = american_colors() %}

select 1 as id

Build and see that everything works just fine:

dbt build -s +model_f

Update models/model_f.sql to add {% set ab_values = a_values + b_values %} anywhere within the model defintion:

{% set a_values = british_colours() %}
{% set b_values = american_colors() %}
{% set ab_values = a_values + b_values %}

select 1 as id

Now re-build and see the error:

dbt build -s +model_f
13:12:43  Completed with 1 error and 0 warnings:
13:12:43  
13:12:43    Compilation Error in unit_test test__model_f (models/unit_test.yml)
  'british_colours' is undefined. This can happen when calling a macro that does not exist. Check for typos and/or install package dependencies with "dbt deps".

Relevant log output

See above

Environment

- OS:
- Python:
- dbt:

Which database adapter are you using with dbt?

postgres

Additional Context

Found while researching https://github.com/dbt-labs/dbt-core/issues/10139.

dbeatty10 avatar May 16 '24 13:05 dbeatty10

Worth confirming would this happen by itself without unittest

ChenyuLInx avatar Jul 15 '24 18:07 ChenyuLInx

This works fine for me if I do not have any dbt unit tests defined:

dbt build -s +model_f

But if I add a dbt unit test and re-run that same command, then it gives the error.

dbeatty10 avatar Jul 15 '24 19:07 dbeatty10

I can confirm that I'm experiencing this as well.

Context

I'm using dbt-core 1.8.3 with dbt-snowflake 1.8.3 on macOS Ventura 13.6.

I'm developing a custom dbt package which defines a suite of macros. I'm experiencing the error in my package's integration_tests project. I have an example model which calls my_package.my_macro(...) in its logic. I've also written an example unit test for the model to verify its functionality. When running the project with dbt run, the model runs as-expected and the compiled code in target/run and target/compiled reflects the expected macro output.

When running either dbt build or dbt test on the project, however, I get the "macro is not defined" error mentioned above.

Please let us know when you've root-caused this bug, since it will be a huge roadblock for me and my team to pivot to using native DBT unit tests as opposed to the EqualExperts unit testing framework (which doesn't seem to suffer from this bug).

Thanks in advance!

dsillman2000 avatar Aug 01 '24 16:08 dsillman2000

Thanks for sharing some of the specifics of your scenario @dsillman2000 👍

If a constant return value is sufficient for the purposes of your unit test, a workaround is to use an overrides configuration like this:

      macros:
        # explicity set star to relevant list of columns
        dbt_utils.star: col_a,col_b,col_c

Otherwise, there isn't a known workaround.

You can stay subscribed to this issue in GitHub to get notified of updates regarding further root cause or resolution.

dbeatty10 avatar Aug 01 '24 21:08 dbeatty10

I am having the same issue, I have a macros named "test_macro.sql":

{% macro test_macro() %} {{ return('This is a test macro') }} {% endmacro %}

in dbt_project.yml I have set up the path correctly and call the macros:

macro-paths: ["macros"] ... generate_schema_name: "{{ test_macro() }}"

But when I run dbt debug I got this error:

Could not render {{ test_macro() }}: 'test_macro' is undefined

Have anyone firgured out with happened?

Goal1803 avatar Sep 11 '24 11:09 Goal1803

@Goal1803 it sounds like you're dealing with a different issue, unrelated to unit tests.

You cannot call a macro directly in the dbt_project.yml as these are parsed at different times. There's a discussion on this here -> https://github.com/dbt-labs/dbt-core/discussions/9172

graciegoheen avatar Sep 18 '24 19:09 graciegoheen

Thanks for sharing some of the specifics of your scenario @dsillman2000 👍

If a constant return value is sufficient for the purposes of your unit test, a workaround is to use an overrides configuration like this:

      macros:
        # explicity set star to relevant list of columns
        dbt_utils.star: col_a,col_b,col_c

Otherwise, there isn't a known workaround.

You can stay subscribed to this issue in GitHub to get notified of updates regarding further root cause or resolution.

I'm running into this issue again, in a new project, with a macro defined within the project (more similar to the minimum reproducible example above). Unfortunately, even setting an overrides configuration for the macro does not fix the issue, as it is still complaining that it's unable to find the macro even when I give it a direct, constant override value to return.

I don't know if this should be filed as a separate bug with the overrides configuration not working, since I've been able to override macros in other projects, but not when encountering the "macro not found" bug. Even when defining an override for the "missing" macro, the parser is still unhappy.

"Overrides" section of the subject unit test case:

unit_tests:
  - name: parse_legacy_tool_events
    description: |-
      Shall correctly parse tool events from legacy software versions into a consistent 
      "SupportLogEvent" format.
    model: stg_support_log_tool_events_legacy
    overrides:
      macros:
        legacy_support_log_sw_versions: 'my_constant_value'
    given: ...

After running dbt test:

'legacy_support_log_sw_versions' is undefined. This can happen when calling a macro that does not exist. Check for typos and/or install package dependencies with "dbt deps".

Running the same model with dbt run works fine, since the macro is valid & defined within the project. But it cannot be found during unit testing, and apparently the override does not apply to it.

As with my testimony above, I'm using dbt-core 1.8.3 and dbt-snowflake 1.8.3 macOS Ventura 13.6.

dsillman2000 avatar Sep 30 '24 14:09 dsillman2000

Moments after posting above, I had a breakthrough @dbeatty10 ! Please try it in your minimum reproducible example project to see if it works / helps you root cause the issue.

Work-around:

  1. Call the macro inside of a set block, e.g.:
{% set legacy_versions %}{{ legacy_support_log_sw_versions() }}{% endset %}
  1. Reference the previously-set value, instead of calling the macro itself:
select * from {{ source(...) }} where software_version_id in ({{ legacy_versions }}) 
  1. ???
  2. Profit!

Note that this also allows the overrides value to propagate correctly. So the bug must be upstream of the overriding stage of dbt's parser. It's also noteworthy that using the return value of the macro in a set tag (i.e. {% set value = macro_name() %}) does not work, but using the set block syntax does work.

This suggests to me that the broken link must be in how dbt is resolving macros differently in a set block context as opposed to other contexts. Please let me know if you're able to reproduce this work-around in your example case above!

dsillman2000 avatar Sep 30 '24 14:09 dsillman2000

I believe I've got a similar issue, we override the source macro to give us the option to include the database when the relation is expanded.

{% macro source(source_name, table_name, include_database=False) %}
  {% do return(builtins.source(source_name, table_name).include(database=include_database)) %}
{% endmacro %}

When running a unit test on a model that includes the additional parameter to source() we get:

10:20:22 Compilation Error in unit_test Daily_TimeSeries_Incremental_UT (models\Nav\Nav.yml) source() takes exactly two arguments (3 given)

Slightly different error message but I suspect it's a similar cause in the custom macros not being evaluated during unit test compilation

HRusby avatar Feb 04 '25 10:02 HRusby

We are really keen to maximise the use of dbt's unit testing. Is there a feature request to support use of 3rd party macros ? We use them extensively and wrapping all the macros in another macro to allow unit testing seems very much like a heavy handed approach to getting this to work ?

The example on the documentation site uses dbt_utils - that also comes from dbt hub. If I use a macro from that package then unit testing works .. assuming i've run dbt deps before hand

WobblyRobbly avatar Feb 06 '25 12:02 WobblyRobbly