如何测试方法并模拟 Flutter 中相同 class 中的另一个方法

How can I test a method and to mock another method that are in the same class in Flutter

描述: 我已经测试了 methodA()methodB(),所以我可以确定它们都被覆盖了。 通过模拟同一文件中的 methodA()methodB() 来测试 methodToBeTested() 的方法有哪些?参数通过 methodToBeTested() 传递给 methodA()methodB() 以使用注入正确测试这些方法。 注意:它们不能被提取到不同的class,因为它是计算服务的相关逻辑,并且这些方法已经原子地分离了。

代码:

class ClassForTesting {
  int methodToBeTested(String a, String b) {
     // Calculation in this method also is a bit more difficult
     return methodA() + methodB();
  } 

  int methodA(String a) {
     int value = 1;
     // Here is calculation logic that has been tested
     return value;
  } 

  int methodB(String b) {
     int value = 2;
     // Here is calculation logic that has been tested
     return value;
  } 
}

已完成: 我已经尝试了 Mockito 的几种方法,但它不允许这样做:

  1. @GenerateMocks - 正在创建模拟并要求我使用 when() 存根每个方法,甚至 methodToBeTested().
  2. 通过使用下一个构造扩展 Fake:
class Mock extends Fake implements PasswordValidatorService {}

但通过这种方式,我只是继承了 PasswordValidatorService's 行为而不是实现,并且每个未覆盖的方法都会抛出 UnimplementedError。因此,我无法覆盖 methodToBeTested() 并调用它的超级实现。

我发现 Java 的 Mockito 具有 @Spy 结构,在这种情况下是完美的,但不幸的是它不适用于 Dart 和 Flutter。

我目前唯一的方法是创建自己的 Mock:

class MockClassForTesting extends ClassForTesting {
  @override
  int methodA() {
    return 2;
  }

  @override
  int methodB() {
    return 5;
  }
}

但是这个实现不允许我使用 Mockito 的 when() 构造的灵活性,因为我必须有不同的 methodA()methodB() returns。 这个事实迫使我在我的 MockClassForTesting 中添加额外的变量来实现 when() 构造功能。

题目:

  1. 实现我的目标的最佳方法是什么?
  2. 可以在 Widget 测试期间使用相同的模拟方法吗?

谢谢!

一种方法是使用混合方法,您可以在其中创建自己的派生 class,但其中一些覆盖委托给 Mock 实现。例如:

class ClassForTesting {
  int methodToBeTested(String a, String b) {
     // Calculation in this method also is a bit more difficult
     return methodA(a) + methodB(b);
  } 

  int methodA(String a) {
     int value = 1;
     // Here is calculation logic that has been tested
     return value;
  } 

  int methodB(String b) {
     int value = 2;
     // Here is calculation logic that has been tested
     return value;
  } 
}

class PartialMockClassForTesting extends ClassForTesting {
  final mock = MockClassForTesting();

  @override
  int methodA(String a) => mock.methodA(a);

  @override
  int methodB(String b) => mock.methodB(b);
}

@GenerateMocks([ClassForTesting])
void main() {
  test('Test partial mock', () {
    var partialMock = PartialMockClassForTesting();
    when(partialMock.methodA('hello')).thenReturn(42);
    when(partialMock.methodA('goodbye')).thenReturn(-42);
    when(partialMock.methodB('world')).thenReturn(10);

    expect(partialMock.methodToBeTested('hello', 'world'), 52);
    expect(partialMock.methodToBeTested('goodbye', 'world'), -32);
  });
}

如果您想有条件地模拟某些方法,您可以让您的覆盖检查布尔标志以有条件地调用模拟或实际实现。例如:

class PartialMockClassForTesting extends ClassForTesting {
  final mock = MockClassForTesting();
  final shouldMock = <Function, bool>{};

  @override
  int methodA(String a) =>
      shouldMock[methodA] ?? false ? mock.methodA(a) : super.methodA(a);

  @override
  int methodB(String b) =>
      shouldMock[methodB] ?? false ? mock.methodB(b) : super.methodB(b);
}

@GenerateMocks([ClassForTesting])
void main() {
  test('Test partial mock', () {
    var partialMock = PartialMockClassForTesting();
    partialMock.shouldMock[partialMock.methodA] = true;
    partialMock.shouldMock[partialMock.methodB] = true;
    ...