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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

False generation of mocks containing generated entities #345

Open
NikoBoerger opened this issue Mar 6, 2021 · 9 comments
Open

False generation of mocks containing generated entities #345

NikoBoerger opened this issue Mar 6, 2021 · 9 comments
Labels
P3 A lower priority bug or feature request type-documentation A request to add or improve documentation

Comments

@NikoBoerger
Copy link

NikoBoerger commented Mar 6, 2021

Hi,

I'm having the following simple class and want to generate a mock for it:

class PlayerModelConverter {
  PlayerTableCompanion convertToPlayerCompanion(Player player);

  Player convertToPlayer(PlayerModel playerModel);
}

"PlayerTableCompanion" and "PlayerModel" are generated classes from the Moor library.
When mocking with @GenerateMocks([PlayerModelConverter]), mockito creates the following:

class MockPlayerModelConverter extends _i1.Mock
    implements _i4.PlayerModelConverter {
  MockPlayerModelConverter() {
    _i1.throwOnMissingStub(this);
  }

  @override
  dynamic convertToPlayerCompanion(_i2.Player? player) => super
      .noSuchMethod(Invocation.method(#convertToPlayerCompanion, [player]));
  @override
  _i2.Player convertToPlayer(dynamic playerModel) =>
      (super.noSuchMethod(Invocation.method(#convertToPlayer, [playerModel]),
          returnValue: _FakePlayer()) as _i2.Player);
}

Both of the moor classes are converted to "dynamic", which obviously doesn't work.

The generated classes from moor look like this:

class PlayerModel extends DataClass implements Insertable<PlayerModel> {
  final int id;
  final String name;
  final String? imagePath;
  final KeyboardVariant? keyboardVariant;
  final ShortcutButtons? shortcutButtons;
  final bool? showCheckoutHints;
  PlayerModel(
      {required this.id,
      required this.name,
      this.imagePath,
      this.keyboardVariant,
      this.shortcutButtons,
      this.showCheckoutHints});
  factory PlayerModel.fromData(Map<String, dynamic> data, GeneratedDatabase db,
      {String? prefix}) {
    final effectivePrefix = prefix ?? '';
    final intType = db.typeSystem.forDartType<int>();
    final stringType = db.typeSystem.forDartType<String>();
    final boolType = db.typeSystem.forDartType<bool>();
    return PlayerModel(
      id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id'])!,
      name: stringType.mapFromDatabaseResponse(data['${effectivePrefix}name'])!,
      imagePath: stringType
          .mapFromDatabaseResponse(data['${effectivePrefix}image_path']),
      keyboardVariant: $PlayerTableTable.$converter0.mapToDart(intType
          .mapFromDatabaseResponse(data['${effectivePrefix}keyboard_variant'])),
      shortcutButtons: $PlayerTableTable.$converter1.mapToDart(stringType
          .mapFromDatabaseResponse(data['${effectivePrefix}shortcut_buttons'])),
      showCheckoutHints: boolType.mapFromDatabaseResponse(
          data['${effectivePrefix}show_checkout_hints']),
    );
  }

[....]

Seems like a normal class to me. Is there a reason why this isn't working? Am I doing something wrong? The code generation works fine for my other mocks, that don't have dependencies to the generated classes from moor.

==============================
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.0.1, on macOS 11.2.2 20D80 darwin-x64, locale de-DE)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.1)
[✓] Connected device (1 available)

Flutter 2.0.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision c5a4b4029c (30 hours ago) • 2021-03-04 09:47:48 -0800
Engine • revision 40441def69
Tools • Dart 2.12.0

mockito: ^5.0.0-nullsafety.7

@srawlins
Copy link
Member

srawlins commented Mar 8, 2021

Can you show me your build configuration? I suspect PlayerTableCompanion and PlayerModel are not generated when Mockito does its generation.

@NikoBoerger
Copy link
Author

What exactly do you mean by build config? How can I influence what gets generated first?

I also see this behavior, when the build runner only has to generate the mocks (and the Moor classes are already generated).

@NikoBoerger
Copy link
Author

Ok I see. Thanks for this hint. I will create a build config and see if that fixes my problem!

@srawlins
Copy link
Member

srawlins commented Mar 8, 2021

Yeah sorry I don't use build_runner or generated files too often. But I suspect there is a way in build.yaml to instruct build_runner to build the other generated files before the mockito files.

If you make progress, I'd love to write up instructions in the FAQ.

@NikoBoerger
Copy link
Author

Okay I got it running with the following build.yaml configuration:

targets:
  $default:
    builders:
      # disables the SharedPartBuilder in favor of a PartBuilder from moor_generator
      moor_generator:
        enabled: false
      moor_generator|moor_generator_not_shared:
        enabled: true
      moor_generator|preparing_builder:
        enabled: true

      # Run mockito when moor is done!
      mockito|mockBuilder:
        enabled: false

  run_built_value:
    dependencies: ['MadHouse']
    builders:
      # Disable moor builders. By default, those would run on each target
      moor_generator:
        enabled: false
      moor_generator|preparing_builder:
        enabled: false
      copy_with_extension_gen|copy_with_extension_gen:
        enabled: false

Unfortunately I don't really have a clue what it does exactly. I found it very hard to find documentation about the build.yaml. I got this one from the moor documentation and adapted it for my usecase: https://moor.simonbinder.eu/docs/advanced-features/builder_options/#using-moor-classes-in-other-builders

It seems like I disable the mockito code generation at first, and somehow the "run_build_value" runs all of the build runners again, after the first builders are done generating. I had to disable all of my other builders in the run_build_value section (copy_with_extension_gen) because it runs twice and conflicts, if I don't disable it there.

The mockito generator is able to detect the classes from moor now and generates the mocks correctly. However, for some of the moor classes mockito seems to have some kind of a problem in the generated class:
image

For all of the *Companion classes I get the syntax error

Object.==' ('bool Function(Object)') isn't a valid concrete implementation of 'UpdateCompanion.==' ('bool Function(dynamic)').

The mentioned "UpdateCompanion" is the super class of the from moor generated "PlayerTableCompanion". UpdateCompanion is from the moor library directly and is not generated.

I don't really understand this error, because I can't find Object.== anywhere in the generated code. Is it a problem you maybe know already?
However, the tests don't seem to care about this error and they all run fine, so it's working for me now. Just the red markers in the project tree indicating the syntax errors are a little annoying :D

@radvansky-tomas
Copy link

Have similar issue with json_serializer plugin and some getters.

Is there any way of "hiding" specific fields / methods via annotation ?

@srawlins
Copy link
Member

srawlins commented Mar 9, 2021

Is there any way of "hiding" specific fields / methods via annotation ?

No, the generated mock needs to be a legal implementation of the given class.

@vast00
Copy link

vast00 commented Jun 10, 2022

Hi guys, I figured it out.
I'm using ferry to generate model classes for GraphQL, so I need to make sure that mockito's builder runs after ferry's.
Here is the full example of build.yaml. It's very similar to NikoBoerger's, but cleaner, and I added some description.

targets:
  $default:
    # This is true by default, so you can just omit this line. This means enable all builders by default. 
    auto_apply_builders: true
    builders:
      # Write your other builders' settings normally here.
      ferry_generator|graphql_builder:
        enabled: true
        options:
          schema: xxx/schema.graphql

      ferry_generator|serializer_builder:
        enabled: true
        options:
          schema: xxx/schema.graphql
          
      # Disable mockito's builder to run it after other code is generated. 
      mockito|mockBuilder:
        enabled: false

  # Adjust this name by yourself. 
  run_mockito:
    # Make sure this target runs after the target above.
    dependencies: ["$default"]
    # Only run mockito's builder. 
    auto_apply_builders: false
    builders:
      mockito|mockBuilder:
        enabled: true

@ghost
Copy link

ghost commented Jun 21, 2023

@vast00 : You are godlike, this works awesome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P3 A lower priority bug or feature request type-documentation A request to add or improve documentation
Projects
None yet
Development

No branches or pull requests

5 participants