Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FeatureRequest] More fully functional BuildStep for tests #45

Open
btrautmann opened this issue Jun 22, 2023 · 1 comment
Open

[FeatureRequest] More fully functional BuildStep for tests #45

btrautmann opened this issue Jun 22, 2023 · 1 comment

Comments

@btrautmann
Copy link

btrautmann commented Jun 22, 2023

Hi there 馃憢 First off, thanks for the great package!

Note: The below context is maybe helpful but not necessary for the overall question. The TLDR question is at the very bottom of this issue.

I have a generator whose purpose is to act on annotated export and function Elements and create fakes of the Type passed into the annotation. It's not too dissimilar to the mockito package.

The usage of the annotation looks like:

@GenerateGraphqlFakes([GHomeData])
export 'my_feature.fakes.dart';

where GHomeData is a type that needs its fake generated. For most of that work, I use a *.data.gql.dart file (generated by ferry), but in some cases there are "special" types whose factories can not be derived simply using the above file. For those, the consumer of my Builder provides a file name (e.g special_type.dart) that is looked up using findAssets on BuildStep. This works at runtime but tests use MockBuildStep which breaks when anything is called on it. To work around this, I've implemented a wrapper generator that injects a FakeBuildStep that implements select functions (specific to my needs). That looks like this:

// in a test

class GeneratorWithFakeBuildStep extends GeneratorForAnnotation<GenerateGraphqlFakes> {
  final FactoryGeneratorConfig config;
  final String customTypesDirectory;

  GeneratorWithFakeBuildStep({
    required this.config,
    required this.customTypesDirectory,
  });

  @override
  Future<String> generateForAnnotatedElement(
    Element element,
    ConstantReader annotation,
    BuildStep buildStep,
  ) {
    final generator = FactoryGenerator(config);
    return generator.generateForAnnotatedElement(
      element,
      annotation,
      FakeBuildStep(customTypesDirectory),
    );
  }
}

FakeBuildStep itself does something similar to what is done when initializing a test LibraryReader:

// fake_build_step.dart

// ignore: subtype_of_sealed_class
class FakeBuildStep extends BuildStep {
  final String testDirectory;
  late final Map<String, String> assets;

  FakeBuildStep(this.testDirectory) {
    final map = Map.fromEntries(
      Directory(testDirectory)
          .listSync(recursive: true)
          .whereType<File>()
          .map((f) => MapEntry(f.path, f.readAsStringSync())),
    );
    String assetIdForFile(String fileName) => '$testPackageName|lib/$fileName';

    assets = map.map((file, content) => MapEntry(assetIdForFile(file), content));
  }
  ...
}

My implementation of findAssets is as follows:

  @override
  Stream<AssetId> findAssets(Glob glob) {
    final keys = assets.keys;
    final matches = keys.expand((assetId) {
      return glob.allMatches(assetId);
    });
    return Stream.fromIterable(
      matches.map(
        (match) => AssetId.parse(match.input),
      ),
    );
  }

So, my primary question is: Is there a reason (other than simplicity) that MockBuildStep was used as the stand-in for the runtime BuildStep? If not, is there any interest in building out a more robust fake that would allow consumers to not need to use a workaround like the above? Or better yet, am I just doing something wrong?

@Jure-BB
Copy link

Jure-BB commented Aug 31, 2023

In my case I need buildStep.resolver.astNodeFor().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants