felangel / mocktail

A mock library for Dart inspired by mockito
https://pub.dev/packages/mocktail
MIT License
588 stars 80 forks source link

Confusing failure with nested mocks #204

Open eseidel opened 11 months ago

eseidel commented 11 months ago

I meant to file this a few weeks ago, sorry.

import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';

class One {
  final Two two = Two();
}

class Two {
  final String three = '';
}

class _MockOne extends Mock implements One {}

class _MockTwo extends Mock implements Two {}

void main() {
  test('nested mocks', () {
    final one = _MockOne();
    final two = _MockTwo();
    when(() => one.two).thenReturn(two);
    when(() => one.two.three).thenReturn('three');
    expect(one.two.three, 'three');
  });
}
00:02 +0 -1: nested mocks [E]                                                                                                         
  type 'String' is not a subtype of type 'Two'
  test/foo_test.dart 12:7   _MockOne.two
  test/foo_test.dart 22:16  main.<fn>

That's a better error than I remember (I remember it being silent?) But it's still pretty confusing. Feel free to close.

eseidel commented 11 months ago

This was the other bug I thought I had, but can't seem to repro now:

import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';

class One {
  final Two two = Two();
}

class Two {
  String three(String arg) => arg;
}

class _MockOne extends Mock implements One {}

class _MockTwo extends Mock implements Two {}

void main() {
  test('nested mocks', () {
    final one = _MockOne();
    final two = _MockTwo();
    when(() => one.two).thenReturn(two);
    when(() => two.three('three')).thenReturn('three');
    one.two.three('three');
    one.two.three('three');
    verify(() => one.two.three('three')).called(2);
  });
}

Where call counts would get double-counted when using nested mocks in the verify() call.

felangel commented 11 months ago

This was the other bug I thought I had, but can't seem to repro now:

import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';

class One {
  final Two two = Two();
}

class Two {
  String three(String arg) => arg;
}

class _MockOne extends Mock implements One {}

class _MockTwo extends Mock implements Two {}

void main() {
  test('nested mocks', () {
    final one = _MockOne();
    final two = _MockTwo();
    when(() => one.two).thenReturn(two);
    when(() => two.three('three')).thenReturn('three');
    one.two.three('three');
    one.two.three('three');
    verify(() => one.two.three('three')).called(2);
  });
}

Where call counts would get double-counted when using nested mocks in the verify() call.

Hmm let me know if you find a way to reproduce it and I’ll check it out 👀

eseidel commented 11 months ago

I suspect I have the curse of knowledge now and just know to avoid these things. :) Thanks!

bryanoltman commented 3 months ago

I had a test succeed when it shouldn't have, I think due to this issue. To reproduce:

code (abbreviated for clarity):

  final deleteReleaseQuery = // the query;
  final deleteReleaseResponse = await bigqueryClient.query(deleteReleaseQuery);

    final deletePatchesQuery = '''
lkajsdfjasdkfjklasdfjklsdajfklasdjklasdfjklasdjf''';
    final deletePatchesResponse =
        await bigqueryClient.query(deletePatchesQuery);

test:

        when(() => bigqueryClient.projectId).thenReturn('test-project-id');
        verifyInOrder([
          () => bigqueryClient.query('''
DELETE `${bigqueryClient.projectId}.command_metadata.release`
WHERE id = ${release.id};'''),
          () => bigqueryClient.query(
                '''
DELETE `${bigqueryClient.projectId}.command_metadata.patch`
WHERE id in (11);''',
              ),
        ]);

This test succeeded because I was referencing the mocked bigqueryClient.projectId in verifyInOrder.

felangel commented 3 months ago

Thanks @bryanoltman!

felangel commented 2 months ago

Also seems related to https://github.com/felangel/mocktail/issues/179