Expressions in an if: key are evaluated with GitHub's own type coercion rules, which follow JavaScript loosely: a non-empty string is truthy. Writing if: ${{ inputs.skip_tests == 'false' }} is fine, but writing if: ${{ inputs.dry_run }} where dry_run is a workflow_dispatch input always evaluates true, because dispatch inputs arrive as strings and the string "false" is non-empty.
The same trap applies to any value that has passed through toJSON, through an output, or through an env variable, since all of those are strings. Job and step outputs are always strings even when the producing expression was a boolean.
Compare explicitly against the string: if: inputs.dry_run == 'true', or use fromJSON(inputs.dry_run) to get a real boolean. Note also that the ${{ }} wrapper is optional in if: and the expression is evaluated either way, so omitting it does not disable evaluation. A workflow_dispatch input declared with type: boolean does arrive as a real boolean in inputs, which is why the same expression behaves differently between dispatch and reusable-workflow calls.