You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mocktail封装stub方法报No method stub was called from when()错误

问题背景

我正在使用 mocktail 0.3.0 包测试HttpAdapter类的request()方法,该方法会调用http包中Client类的post()方法。
初始业务代码如下:

import 'dart:io';
import 'package:http/http.dart';

class HttpAdapter {
  static const headers = {
    HttpHeaders.contentTypeHeader: 'application/json',
    HttpHeaders.acceptHeader: 'application/json',
  };

  final Client client;

  HttpAdapter(this.client);

  Future<void> request({
    required String url,
    required String method,
    HttpClientBody? body,
  }) async {
    await client.post(Uri.parse(url), headers: HttpAdapter.headers);
  }
}

class ClientSpy extends Mock implements Client {
  void mockPost(String url) {
    when(() => post(
          Uri.parse(url),
          headers: any(named: 'headers'),
        )).thenAnswer(
      (_) async => Response('{}', HttpStatus.ok),
    );
  }
}

直接在测试中编写when打桩逻辑时,用例可正常运行:

void main() {
 test('Should call post() with correct parameters', () async {
   // Arrange
   final client = ClientSpy();
   final sut = HttpAdapter(client);

   when(() => client.post(
         Uri.parse(url),
         headers: HttpAdapter.headers,
       )).thenAnswer(
     (_) async => Response('{}', HttpStatus.ok),
   );
   // Act
   await sut.request(url: url, method: method);
   // Assert
   verify(() => client.post(
         Uri.parse(url),
         headers: HttpAdapter.headers,
       ));
  });
}

但将when()打桩语句封装为mockPost()方法调用时,测试会抛出错误:

Bad state: No method stub was called from within 'when()'. Was a real method called, or perhaps an extension method?

对应报错的测试代码如下:

void main() {
 test('Should call post() with correct parameters', () async {
   // Arrange
   final client = ClientSpy();
   final sut = HttpAdapter(client);

   client.mockPost();
   // Act
   await sut.request(url: url, method: method);
   // Assert
   verify(() => client.post(
         Uri.parse(url),
         headers: HttpAdapter.headers,
       ));
  });
}

错误原因

代码存在两个核心问题:

  • 方法调用绑定错误:http包自带顶层post()函数,在ClientSpy的mockPost方法中直接写post(...)时,Dart会优先解析为调用http包的顶层真实网络请求方法,而非当前mock实例的post()实例方法。mocktail依赖拦截mock实例的方法调用来完成打桩,真实方法调用不会被拦截,因此抛出No method stub was called错误。
  • 参数缺失:mockPost方法定义了必填的String url参数,但测试中调用client.mockPost()时未传入该参数,本身不符合方法调用要求。

修复方案
  1. 修改ClientSpy的打桩逻辑,给被mock的post方法加上this.前缀,强制指定调用当前mock实例的方法:
class ClientSpy extends Mock implements Client {
  void mockPost(String url) {
    // 增加this.前缀,避免误调用顶层post函数
    when(() => this.post(
          Uri.parse(url),
          headers: any(named: 'headers'),
        )).thenAnswer(
      (_) async => Response('{}', HttpStatus.ok),
    );
  }
}
  1. 测试中调用mockPost时,传入对应的测试url参数:
void main() {
  // 提前定义测试常量
  const testUrl = 'https://example.com/test';
  const testMethod = 'post';

 test('Should call post() with correct parameters', () async {
   // Arrange
   final client = ClientSpy();
   final sut = HttpAdapter(client);
   // 传入必填的url参数
   client.mockPost(testUrl);
   // Act
   await sut.request(url: testUrl, method: testMethod);
   // Assert
   verify(() => client.post(
         Uri.parse(testUrl),
         headers: HttpAdapter.headers,
       ));
  });
}

修改后测试即可正常运行。

内容的提问来源于stack exchange,提问作者Eyder Rios

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 13:45:32