mockito
mockito copied to clipboard
False generation of mocks containing generated entities
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
Can you show me your build configuration? I suspect PlayerTableCompanion and PlayerModel are not generated when Mockito does its generation.
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).
Ok I see. Thanks for this hint. I will create a build config and see if that fixes my problem!
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.
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:
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
Have similar issue with json_serializer plugin and some getters.
Is there any way of "hiding" specific fields / methods via annotation ?
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.
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
@vast00 : You are godlike, this works awesome