Use Mocking Package for Dio intended to Be Used In Tests For Flutter

Description

http_mock_adapter is a simple to use mocking package for Dio intended to be used in tests. It provides various types and methods to declaratively mock request-response communication.

Usage

Here is a very basic usage scenario:

import 'package:dio/dio.dart';
import 'package:http_mock_adapter/http_mock_adapter.dart';

void main() async {
  final dio = Dio(BaseOptions());
  final dioAdapter = DioAdapter(dio: dio);

  const path = 'https://example.com';

  dioAdapter.onGet(
    path,
    (server) => server.reply(
      200,
      {'message': 'Success!'},
      // Reply would wait for one-sec before returning data.
      delay: const Duration(seconds: 1),
    ),
  );

  final response = await dio.get(path);

  print(response.data); // {message: Success!}
}

Real-world example

The intended usage domain is in tests when trying to simulate behavior of request-response communication with a server. The example portrays a decent use case of how one might make good use of the package.

Installing

Quick install

You can quickly install the package from the command-line:

With dart:

$ dart pub add --dev http_mock_adapter
...

With flutter:

$ flutter pub add --dev http_mock_adapter
...

Manual install

Depend on it

Add this to your package's pubspec.yaml file:

dev_dependencies:
  http_mock_adapter: ^0.4.4

Install it

You can then install the package from the command-line:

With dart:

$ dart pub get
...

With flutter:

$ flutter pub get
...

Alternatively, your editor might support dart pub get or flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:http_mock_adapter/http_mock_adapter.dart';

Changelog

All notable changes to this project will be documented in the CHANGELOG.md file.

Authors

See the AUTHORS file for information regarding the authors of the project.

License

http-mock-adapter is licensed under the permissive MIT License (LICENSE).

Contribution

For information regarding contributions, please refer to CONTRIBUTING.md file.

Use this package as a library

Depend on it

Run this command:

With Dart:

 $ dart pub add http_mock_adapter

With Flutter:

 $ flutter pub add http_mock_adapter

This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get):

dependencies:
  http_mock_adapter: ^0.4.4

Alternatively, your editor might support dart pub get or flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:http_mock_adapter/http_mock_adapter.dart';

example/main.dart

import 'package:dio/dio.dart';
import 'package:http_mock_adapter/http_mock_adapter.dart';
import 'package:test/test.dart';

void main() async {
  late Dio dio;
  late DioAdapter dioAdapter;

  Response<dynamic> response;

  group('Accounts', () {
    const baseUrl = 'https://example.com';

    const userCredentials = <String, dynamic>{
      'email': 'test@example.com',
      'password': 'password',
    };

    setUp(() {
      dio = Dio(BaseOptions(baseUrl: baseUrl));
      dioAdapter = DioAdapter(
        dio: dio,

        // [FullHttpRequestMatcher] is a default matcher class
        // (which actually means you haven't to pass it manually) that matches entire URL.
        //
        // Use [UrlRequestMatcher] for matching request based on the path of the URL.
        //
        // Or create your own http-request matcher via extending your class from  [HttpRequestMatcher].
        // See -> issue:[124] & pr:[125]
        matcher: const FullHttpRequestMatcher(),
      );
    });

    test('signs up user', () async {
      const route = '/signup';

      dioAdapter.onPost(
        route,
        (server) => server.reply(
          201,
          null,
          // Adds one-sec delay to reply method.
          // Basically, I'd wait for one second before returning reply data.
          // See -> issue:[106] & pr:[126]
          delay: const Duration(seconds: 1),
        ),
        data: userCredentials,
      );

      // Returns a response with 201 Created success status response code.
      response = await dio.post(route, data: userCredentials);

      expect(response.statusCode, 201);
    });

    test('signs in user and fetches account information', () async {
      const signInRoute = '/signin';
      const accountRoute = '/account';

      const accessToken = <String, dynamic>{
        'token': 'ACCESS_TOKEN',
      };

      final headers = <String, dynamic>{
        'Authentication': 'Bearer $accessToken',
      };

      const userInformation = <String, dynamic>{
        'id': 1,
        'email': 'test@example.com',
        'password': 'password',
        'email_verified': false,
      };

      dioAdapter
        ..onPost(
          signInRoute,
          (server) => server.throws(
            401,
            DioError(
              requestOptions: RequestOptions(
                path: signInRoute,
              ),
            ),
          ),
        )
        ..onPost(
          signInRoute,
          (server) => server.reply(200, accessToken),
          data: userCredentials,
        )
        ..onGet(
          accountRoute,
          (server) => server.reply(200, userInformation),
          headers: headers,
        );

      // Throws without user credentials.
      expect(
        () async => await dio.post(signInRoute),
        throwsA(isA<DioError>()),
      );

      // Returns an access token if user credentials are provided.
      response = await dio.post(signInRoute, data: userCredentials);

      expect(response.data, accessToken);

      // Returns user information if an access token is provided in headers.
      response = await dio.get(
        accountRoute,
        options: Options(headers: headers),
      );

      expect(response.data, userInformation);
    });
  });
}

Download Details:

Author: lomsa.com

Source Code: https://github.com/lomsa-dev/http-mock-adapter

#flutter #android #ios #http #mock #testing #test 

Use Mocking Package for Dio intended to Be Used In Tests For Flutter

Создайте фиктивный сервер API с помощью Postman

Postman — это мощный инструмент, который можно использовать в качестве фиктивного сервера для создания фиктивных служб, доступных как в общедоступных, так и в частных сетях. Это делает его очень полезным инструментом для создания фиктивных сервисов, которые можно использовать для тестирования пользовательского интерфейса и других компонентов, не дожидаясь готовности фактического сервиса. Имитируя поведение реальной службы, Postman позволяет разработчикам тестировать свой код в реальной среде, помогая выявлять потенциальные проблемы до развертывания службы.

Настройка мок-сервера Postman

  • Установите Postman на виртуальную машину или свою собственную машину в зависимости от варианта использования, используя ссылку  https://www.postman.com/downloads/.
  • Открытый почтальон.
  • Нажмите кнопку «...» в правом верхнем углу экрана и выберите «Mock Server» в раскрывающемся меню.
    Создайте фиктивный сервер API с помощью Postman
  • В окне «Мок-сервер» выберите « Создать фиктивный сервер », если вы его еще не создали.
  • Выберите запрос, который вы хотите использовать в качестве шаблона для своего фиктивного сервера, и нажмите кнопку « Создать ».
  • В окне « Mock Server » выберите имя для своего фиктивного сервера и среду, в которой вы хотите его запустить.
  • На вкладке « Поведение » выберите ответы, которые вы хотите, чтобы ваш фиктивный сервер возвращал. Вы можете добавить определенные ответы, случайные ответы или даже создать собственные сценарии для генерации ответов.
  • На вкладке « Примеры » вы можете добавить примеры ответов, которые помогут вам протестировать запрос API.
  • Когда вы закончите настройку фиктивного сервера, нажмите кнопку « Сохранить », чтобы сохранить изменения.
  • Затем вы можете запустить свой фиктивный сервер, нажав кнопку «Пуск» в окне « Мок-сервер ».

После того, как ваш фиктивный сервер запущен и запущен, вы можете использовать его для тестирования запросов API, отправляя их на URL-адрес, предоставленный фиктивным сервером. Ответы, которые вы получите, будут основаны на поведении, которое вы указали при настройке фиктивного сервера. Это позволяет вам тестировать ваши запросы API, не дожидаясь готовности фактического сервиса.

Преимущества Postman Mock Server

  • Обеспечивает более быструю разработку:  с фиктивным сервером разработчики могут тестировать свой код в реальной среде, не дожидаясь, пока реальная служба станет доступной. Это может значительно ускорить процесс разработки и сократить время, необходимое для развертывания API.
  • Обеспечивает реалистичную среду тестирования . Имитируя поведение реальной службы, фиктивный сервер Postman обеспечивает среду тестирования, максимально приближенную к реальной. Это может помочь выявить потенциальные проблемы на ранних этапах процесса разработки и гарантировать, что API работают должным образом.
  • Снижает затраты на разработку : выявляя проблемы на ранних стадиях разработки, макетные серверы Postman помогают снизить затраты, связанные с исправлением ошибок и другими проблемами после развертывания. Кроме того, использование фиктивного сервера может помочь сократить количество ресурсов, необходимых для тестирования, поскольку разработчики могут тестировать свой код в смоделированной среде вместо того, чтобы раскручивать дополнительную инфраструктуру.
  • Обеспечивает гибкость и масштабируемость : фиктивные серверы Postman можно легко настроить для имитации широкого спектра действий, что упрощает тестирование различных сценариев и пограничных случаев. Кроме того, фиктивные серверы можно увеличивать или уменьшать по мере необходимости, что позволяет разработчикам тестировать свой код на различных уровнях нагрузки.
  • Улучшает совместную работу и общение:  с макетным сервером Postman разработчики могут легко делиться своим кодом с другими членами своей команды и получать отзывы на ранних этапах процесса разработки. Это может помочь улучшить совместную работу и гарантировать, что все находятся на одной странице, когда дело доходит до разработки API.

Краткое содержание

Я надеюсь, что из приведенного выше объяснения вы узнали, как создавать фиктивные службы API с помощью фиктивного сервера Postman.

Оригинальный источник статьи: https://www.c-sharpcorner.com/

#postman #mock #api #server 

Создайте фиктивный сервер API с помощью Postman
佐藤  桃子

佐藤 桃子

1683091080

使用 Postman 创建模拟 API 服务器

Postman 是一个强大的工具,可以用作模拟服务器来创建可通过公共和专用网络访问的模拟服务。这使它成为创建模拟服务的非常有用的工具,可用于测试 UI 和其他组件,而无需等待实际服务准备就绪。通过模拟真实服务的行为,Postman 允许开发人员针对真实环境测试他们的代码,帮助在部署服务之前识别潜在问题。

设置邮递员模拟服务器

  • 使用链接https://www.postman.com/downloads/,根据用例在 VM 或您自己的计算机上安装 Postman  。
  • 打开邮递员。
  • 单击屏幕右上角的“...”按钮,然后从下拉菜单中选择“模拟服务器”。
    使用 Postman 创建模拟 API 服务器
  • 如果您还没有创建模拟服务器,请在“模拟服务器”窗口中选择“创建模拟服务器”。
  • 选择要用作模拟服务器模板的请求,然后选择“创建”按钮。
  • 在“ Mock Server ”窗口中,为您的模拟服务器选择一个名称,并选择您要运行它的环境。
  • 在“行为”选项卡下,选择您希望模拟服务器返回的响应。您可以选择添加特定响应、随机响应,甚至可以创建自定义脚本来生成响应。
  • 在“示例”选项卡下,您可以添加示例响应以帮助您测试 API 请求。
  • 完成模拟服务器的配置后,选择“保存”按钮以保存您的更改。
  • 然后,您可以通过选择“ Mock Server ”窗口中的“Start”按钮来启动您的模拟服务器。

一旦您的模拟服务器启动并运行,您就可以使用它来测试您的 API 请求,方法是将它们发送到模拟服务器提供的 URL。您收到的响应将基于您在设置模拟服务器时指定的行为。这使您可以测试 API 请求,而无需等待实际服务准备就绪。

Postman Mock Server 的优势

  • 实现更快的开发: 使用模拟服务器,开发人员可以针对真实环境测试他们的代码,而无需等待实际服务可用。这可以大大加快开发过程并减少部署 API 所需的时间。
  • 提供真实的测试环境:通过模拟真实服务的行为,Postman 模拟服务器提供了一个尽可能接近真实事物的测试环境。这有助于在开发过程的早期识别潜在问题,并确保 API 按预期工作。
  • 降低开发成本:通过在开发过程的早期发现问题,Postman 模拟服务器可以帮助降低与修复错误和部署后其他问题相关的成本。此外,使用模拟服务器有助于减少测试所需的资源数量,因为开发人员可以在模拟环境中测试他们的代码,而不必启动额外的基础设施。
  • 提供灵活性和可扩展性:可以轻松配置 Postman 模拟服务器来模拟各种行为,从而轻松测试不同的场景和边缘情况。此外,模拟服务器可以根据需要放大或缩小,允许开发人员针对各种不同的负载级别测试他们的代码。
  • 改善协作和沟通: 使用 Postman 模拟服务器,开发人员可以轻松地与团队的其他成员共享他们的代码,并在开发过程的早期获得反馈。这有助于改善协作并确保在 API 开发方面每个人都在同一页面上。

概括

我希望从上面的解释中你已经学会了如何使用 Postman 模拟服务器创建模拟 API 服务。

文章原文出处:https: //www.c-sharpcorner.com/

#postman #mock #api #server 

使用 Postman 创建模拟 API 服务器

Create Mock API Server Using Postman

The Postman is a powerful tool that can be used as a mock server to create mock services that are accessible over both public and private networks. This makes it a very useful tool for creating mock services that can be used to test the UI and other components without having to wait for the actual service to be ready. By simulating the behaviour of a real service, Postman allows developers to test their code against a realistic environment, helping to identify potential issues before the service is deployed.

Setting Up a Postman Mock Server

  • Install Postman on a VM or your own machine based on the use case using the link  https://www.postman.com/downloads/.
  • Open Postman.
  • Click on the "..." button in the top right corner of the screen and select "Mock Server" from the drop-down menu.
    Create Mock API Server Using Postman
  • In the "Mock Server" window, select "Create a mock server" if you haven't created one yet.
  • Choose the request that you want to use as a template for your mock server, and select the "Create" button.
  • In the "Mock Server" window, choose a name for your mock server, and select the environment in which you want to run it.
  • Under the "Behaviour" tab, select the responses that you want your mock server to return. You can choose to add specific responses, random responses, or even create custom scripts to generate responses.
  • Under the "Examples" tab, you can add example responses to help you test your API request.
  • Once you're done configuring your mock server, select the "Save" button to save your changes.
  • You can then start your mock server by selecting the "Start" button in the "Mock Server" window.

Once your mock server is up and running, you can use it to test your API requests by sending them to the URL provided by the mock server. The responses you receive will be based on the behaviour you specified when you set up the mock server. This allows you to test your API requests without having to wait for the actual service to be ready.

Advantages of Postman Mock Server

  • Enables faster development: With a mock server, developers can test their code against a realistic environment without having to wait for the actual service to be available. This can greatly speed up the development process and reduce the time required to deploy APIs.
  • Provides a realistic testing environment: By simulating the behaviour of a real service, a Postman mock server provides a testing environment that is as close to the real thing as possible. This can help identify potential issues early in the development process and ensure that APIs work as intended.
  • Reduces development costs: By catching issues early in the development process, Postman mock servers can help reduce the costs associated with fixing bugs and other issues after deployment. Additionally, using a mock server can help reduce the number of resources required for testing, as developers can test their code in a simulated environment instead of having to spin up additional infrastructure.
  • Provides flexibility and scalability: Postman mock servers can be easily configured to simulate a wide range of behaviours, making it easy to test different scenarios and edge cases. Additionally, mock servers can be scaled up or down as needed, allowing developers to test their code against a variety of different load levels.
  • Improves collaboration and communication: With a Postman mock server, developers can easily share their code with other members of their team and get feedback early in the development process. This can help improve collaboration and ensure that everyone is on the same page when it comes to API development.

Summary

I hope from the above explanation you have learned how to create mock API services using the Postman mock server.

Original article source at: https://www.c-sharpcorner.com/

#postman #mock #api #server 

Create Mock API Server Using Postman

A Dart mock library which simplifies mocking with null safety support

Mock library for Dart inspired by mockito.

Mocktail focuses on providing a familiar, simple API for creating mocks in Dart (with null-safety) without the need for manual mocks or code generation.

Creating a Mock

import 'package:mocktail/mocktail.dart';

// A Real Cat class
class Cat {
  String sound() => 'meow!';
  bool likes(String food, {bool isHungry = false}) => false;
  final int lives = 9;
}

// A Mock Cat class
class MockCat extends Mock implements Cat {}

void main() {
  // Create a Mock Cat instance
  final cat = MockCat();
}

Stub and Verify Behavior

The MockCat instance can then be used to stub and verify calls.

// Stub the `sound` method.
when(() => cat.sound()).thenReturn('meow');

// Verify no interactions have occurred.
verifyNever(() => cat.sound());

// Interact with the mock cat instance.
cat.sound();

// Verify the interaction occurred.
verify(() => cat.sound()).called(1);

// Interact with the mock instance again.
cat.sound();

// Verify the interaction occurred twice.
verify(() => cat.sound()).called(2);

Additional Usage

// Stub a method before interacting with the mock.
when(() => cat.sound()).thenReturn('purrr!');
expect(cat.sound(), 'purrr!');

// You can interact with the mock multiple times.
expect(cat.sound(), 'purrr!');

// You can change the stub.
when(() => cat.sound()).thenReturn('meow!');
expect(cat.sound(), 'meow');

// You can stub getters.
when(() => cat.lives).thenReturn(10);
expect(cat.lives, 10);

// You can stub a method for specific arguments.
when(() => cat.likes('fish', isHungry: false)).thenReturn(true);
expect(cat.likes('fish', isHungry: false), isTrue);

// You can verify the interaction for specific arguments.
verify(() => cat.likes('fish', isHungry: false)).called(1);

// You can stub a method using argument matcher: `any`.
// When stubbing a positional argument, use `any()`.
// When stubbing a named argument, use `any(named: '<argName>`)`.
// A custom matcher can be provided using `any(that: customMatcher)`.
when(() => cat.likes(any(), isHungry: any(named: 'isHungry', that: isFalse)).thenReturn(true);
expect(cat.likes('fish', isHungry: false), isTrue);

// You can stub a method to throw.
when(() => cat.sound()).thenThrow(Exception('oops'));
expect(() => cat.sound(), throwsA(isA<Exception>()));

// You can calculate stubs dynamically.
final sounds = ['purrr', 'meow'];
when(() => cat.sound()).thenAnswer((_) => sounds.removeAt(0));
expect(cat.sound(), 'purrr');
expect(cat.sound(), 'meow');

// You can capture any argument.
when(() => cat.likes('fish')).thenReturn(true);
expect(cat.likes('fish'), isTrue);
final captured = verify(() => cat.likes(captureAny())).captured;
expect(captured.last, equals(['fish']));

// You can capture a specific argument based on a matcher.
when(() => cat.likes(any())).thenReturn(true);
expect(cat.likes('fish'), isTrue);
expect(cat.likes('dog food'), isTrue);
final captured = verify(() => cat.likes(captureAny(that: startsWith('d')))).captured;
expect(captured.last, equals(['dog food']));

Resetting Mocks

reset(cat); // Reset stubs and interactions

How it works

Mocktail uses closures to handle catching TypeError instances which would otherwise propagate and cause test failures when stubbing/verifying non-nullable return types. Check out #24 for more information.

In order to support argument matchers such as any and captureAny mocktail has to register default fallback values to return when the argument matchers are used. Out of the box, it automatically handles all primitive types, however, when using argument matchers in place of custom types developers must use registerFallbackValue to provide a default return value. It is only required to call registerFallbackValue once per type so it is recommended to place all registerFallbackValue calls within setUpAll.

class Food {...}

class Cat {
  bool likes(Food food) {...}
}

...

class MockCat extends Mock implements Cat {}

class FakeFood extends Fake implements Food {}

void main() {
  setUpAll(() {
    registerFallbackValue(FakeFood());
  });

  test('...', () {
    final cat = MockCat();
    when(() => cat.likes(any()).thenReturn(true);
    ...
  });
}

FAQs

Why am I getting an invalid_implementation_override error when trying to Fake certain classes like ThemeData and ColorScheme?

Relevant Issue

This is likely due to differences in the function signature of toString for the class and can be resolved using a mixin as demonstrated below:

mixin DiagnosticableToStringMixin on Object {
  @override
  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
    return super.toString();
  }
}

class FakeThemeData extends Fake
  with DiagnosticableToStringMixin
  implements ThemeData {}

Why can't I stub/verify extension methods properly?

Relevant Issue

Extension methods cannot be stubbed/verified as they are treated like static methods. This means that calls go directly to the extension method without caring about the instance. As a result, stubs and verify calls to extensions always result in an invocation of the real extension method.

Instead of stubbing/verifying extension methods directly, prefer to stub/verify public members on the instance with which the extension methods interact.

Why am I seeing error: type 'Null' is not a subtype of type 'Future

Relevant Issue

By default when a class extends Mock any unstubbed methods return null.

For example, take the following class:

class Person {
  Future<void> sleep() {
    await Future<void>.delayed(Duration(hours: 8));
  }
}

We can create a MockPerson like:

class MockPerson extends Mock implements Person {}

If we have code that invokes sleep on MockPerson we will get a TypeError:

type 'Null' is not a subtype of type 'Future<void>'

This is because we did not stub sleep so when sleep is called on an instance of MockPerson, mocktail returns null which is not compatible with Future<void>.

To address this, we must explicitly stub sleep like:

final person = MockPerson();
when(person.sleep).thenAnswer((_) async {});

Use this package as a library

Depend on it

Run this command:

With Dart:

 $ dart pub add mocktail

With Flutter:

 $ flutter pub add mocktail

This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get):

dependencies:
  mocktail: ^0.3.0

Alternatively, your editor might support dart pub get or flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:mocktail/mocktail.dart';

example/main.dart

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

class Food {}

class Chicken extends Food {}

class Tuna extends Food {}

// A Real Cat class
class Cat {
  String sound() => 'meow!';
  bool likes(String food, {bool isHungry = false}) => false;
  void eat<T extends Food>(T food) {}
  final int lives = 9;
}

// A Mock Cat class
class MockCat extends Mock implements Cat {}

void main() {
  group('Cat', () {
    setUpAll(() {
      // Register fallback values when using
      // `any` or `captureAny` with custom objects.
      registerFallbackValue(Chicken());
      registerFallbackValue(Tuna());
    });

    late Cat cat;

    setUp(() {
      cat = MockCat();
    });

    test('example', () {
      // Stub a method before interacting with the mock.
      when(() => cat.sound()).thenReturn('purr');

      // Interact with the mock.
      expect(cat.sound(), 'purr');

      // Verify the interaction.
      verify(() => cat.sound()).called(1);

      // Stub a method with parameters
      when(
        () => cat.likes('fish', isHungry: any(named: 'isHungry')),
      ).thenReturn(true);
      expect(cat.likes('fish', isHungry: true), isTrue);

      // Verify the interaction.
      verify(() => cat.likes('fish', isHungry: true)).called(1);

      // Interact with the mock.
      cat
        ..eat(Chicken())
        ..eat(Tuna());

      // Verify the interaction with specific type arguments.
      verify(() => cat.eat<Chicken>(any())).called(1);
      verify(() => cat.eat<Tuna>(any())).called(1);
      verifyNever(() => cat.eat<Food>(any()));
    });
  });
}

Download Details:

Author: felangel.dev

Source Code: https://github.com/felangel/mocktail

#flutter #android #ios #mock #dart 

A Dart mock library which simplifies mocking with null safety support

Как использовать макеты в Python

Безопасно протестируйте свой код с помощью макетов.

1 апреля — это фальшивые истории и притворство. Это делает этот день идеальным, чтобы поговорить о насмешках.

Иногда использование реальных объектов затруднено, опрометчиво или сложно. Например, requests.Sessionподключается к реальным веб-сайтам. Использование его в ваших юнит-тестах вызывает… много… проблем.

Основные насмешки в Python

«Моки» — это концепция юниттеста. Они производят объекты, заменяющие настоящие.

from unittest import mock

Существует целая индустрия, объясняющая, что «фиктивный», «фальшивый» и «заглушка» слегка различаются. В этой статье я использую эти термины взаимозаменяемо.

regular = mock.MagicMock()

def do_something(o):
    return o.something(5)

do_something(regular)

Этот код производит:

<MagicMock name='mock.something()' id='140228824621520'>

Моки имеют все методы. Методы обычно возвращают другой Mock. Это можно изменить, назначив его return_value.

Например, предположим, что вы хотите вызвать следующую функцию:

def do_something(o):
    return o.something() + 1

Это требует чего-то, что имеет .something()метод. К счастью, у фиктивных объектов это есть:

obj = mock.MagicMock(name="an object")
obj.something.return_value = 2
print(do_something(obj))

Ответ:

3

Также возможно переопределить «магические методы»:

a = mock.MagicMock()
a.__str__.return_value = "an a"
print(str(a))

Ответ:

an a

Спецификация

Убедитесь, что макет не имеет «лишних» методов или атрибутов, используя спецификацию. Например, вот код, который должен дать сбой:

import pathlib

def bad_pathlib_usage(path):
    ## TYPO: missing underscore
    path.writetext("hello")

dummy_path = mock.MagicMock(spec=pathlib.Path)

try:
    bad_pathlib_usage(dummy_path)
except Exception as exc:
    print("Failed!", repr(exc))

Результат:

Failed! AttributeError("Mock object has no attribute 'writetext'")

Ложный побочный эффект

Иногда наличие , MagicMockкоторое каждый раз возвращает одно и то же, не совсем то, что вам нужно. Например, sys.stdin.readline()обычно возвращает разные значения, а не одно и то же значение на протяжении всего теста.

Свойство side_effectпозволяет контролировать то, что возвращает магический макет, на более детальном уровне, чем использование return_value.

Повторяемый

Одной из вещей, которым можно присвоить, side_effectявляется итерируемый объект , такой как последовательность или генератор.

Это мощная функция. Это позволяет контролировать возвращаемое значение каждого вызова с небольшим количеством кода.

different_things = mock.MagicMock()
different_things.side_effect = [1, 2, 3]
print(different_things())
print(different_things())
print(different_things())

Выход:

1
2
3

Более реалистичным примером является имитация ввода файла. В этом случае я хочу иметь возможность контролировать, что readlineвозвращается каждый раз, чтобы притвориться, что это ввод файла:

def parse_three_lines(fpin):
    line = fpin.readline()
    name, value = line.split()
    modifier = fpin.readline().strip()
    extra = fpin.readline().strip()
    return {name: f"{value}/{modifier}+{extra}"}

from io import TextIOBase
    
filelike = mock.MagicMock(spec=TextIOBase)
filelike.readline.side_effect = [
    "thing important\n",
    "a-little\n",
    "to-some-people\n"
]
value = parse_three_lines(filelike)
print(value)

Результат:

{'thing': 'important/a-little+to-some-people'}

Исключение

Еще одна возможная вещь — назначить исключение для side_effectатрибута. Это приводит к тому, что вызов вызывает назначенное вами исключение. Использование этой функции позволяет моделировать граничные условия в среде, обычно именно такие, которые:

  • Вы заботитесь о
  • Трудно смоделировать реалистично

Одним из популярных случаев являются проблемы с сетью. Согласно закону Мерфи, они всегда происходят в 4 часа утра, вызывая срабатывание пейджера, и никогда в 10 часов, когда вы сидите за своим столом. Нижеследующее основано на реальном коде, который я написал для тестирования сетевой службы.

В этом упрощенном примере код возвращает длину строки ответа или отрицательное число, если истекло время ожидания. Число отличается в зависимости от того, когда оно было достигнуто при согласовании протокола. Это позволяет коду отличать, например, «время ожидания соединения» от «время ожидания ответа».

Тестировать этот код на реальном сервере сложно. Серверы изо всех сил стараются избегать простоев! Вы можете разветвить код C сервера и добавить немного хаоса, или вы можете просто использовать side_effectи издеваться:

import socket

def careful_reader(sock):
    sock.settimeout(5)
    try:
        sock.connect(("some.host", 8451))
    except socket.timeout:
        return -1
    try:
        sock.sendall(b"DO THING\n")
    except socket.timeout:
        return -2
    fpin = sock.makefile()
    try:
        line = fpin.readline()
    except socket.timeout:
        return -3
    return len(line.strip())

from io import TextIOBase
from unittest import mock

sock = mock.MagicMock(spec=socket.socket)
sock.connect.side_effect = socket.timeout("too long")
print(careful_reader(sock))

Результат — сбой, что в данном случае означает успешный тест:

-1

С осторожными побочными эффектами вы можете добраться до каждого из возвращаемых значений. Например:

sock = mock.MagicMock(spec=socket.socket)
sock.sendall.side_effect = socket.timeout("too long")
print(careful_reader(sock))

Результат:

-2

Вызываемый

Предыдущий пример упрощен. Тестовый код реальной сетевой службы должен проверять правильность полученных результатов, чтобы убедиться, что сервер работает правильно. Это означает выполнение синтетического запроса и поиск правильного результата. Мок-объект должен подражать этому. Он должен выполнять некоторые вычисления на входах.

Попытка протестировать такой код без выполнения каких-либо вычислений затруднительна. Тесты имеют тенденцию быть слишком нечувствительными или слишком «ненадежными».

  • Нечувствительный тест — это тот, который не дает сбоев при наличии ошибок.
  • Ненадежный тест — это тест, который иногда дает сбой, даже если код правильный.

Здесь мой код неверен. Нечувствительный тест его не улавливает, а нестабильный тест провалится, даже если он будет исправлен!

import socket
import random

def yolo_reader(sock):
    sock.settimeout(5)
    sock.connect(("some.host", 8451))
    fpin = sock.makefile()
    order = [0, 1]
    random.shuffle(order)
    while order:
        if order.pop() == 0:
            sock.sendall(b"GET KEY\n")
            key = fpin.readline().strip()
        else:
            sock.sendall(b"GET VALUE\n")
            value = fpin.readline().strip()
    return {value: key} ## Woops bug, should be {key: value}

Следующее было бы слишком «бесчувственным», не обнаруживая ошибку:

sock = mock.MagicMock(spec=socket.socket)
sock.makefile.return_value.readline.return_value = "interesting\n"
assert yolo_reader(sock) == {"interesting": "interesting"}

Следующее было бы слишком «ненадежным», обнаруживая ошибку, даже если ее нет, иногда:

for i in range(10):
    sock = mock.MagicMock(spec=socket.socket)
    sock.makefile.return_value.readline.side_effect = ["key\n", "value\n"]
    if yolo_reader(sock) != {"key": "value"}:
        print(i, end=" ")

Например:

3 6 7 9 

Последний вариант получения результатов от фиктивного объекта — назначить вызываемый объект side_effect. Это призывает side_effectпросто назвать это. Почему бы просто не назначить вызываемый объект непосредственно атрибуту? Наберитесь терпения, я доберусь до этого в следующей части!

В этом примере мой вызываемый объект (просто функция) присваивает a return_valueатрибуту другого объекта. Это не такая уж редкость. Я моделирую среду, а в реальной среде нажатие на одну вещь часто влияет на другие вещи.

sock = mock.MagicMock(spec=socket.socket)
def sendall(data):
    cmd, name = data.decode("ascii").split()
    if name == "KEY":
        sock.makefile.return_value.readline.return_value = "key\n"
    elif name == "VALUE":
        sock.makefile.return_value.readline.return_value = "value\n"
    else:
        raise ValueError("got bad command", name)
sock.sendall.side_effect = sendall
print(yolo_reader(sock), dict(key="value"))

Результат:

{'value': 'key'} {'key': 'value'}

Аргументы фиктивного вызова: рентген для кода

При написании модульного теста вы находитесь «вдали» от кода, но пытаетесь заглянуть в его нутро, чтобы увидеть, как он себя ведет. Объект Mock — ваш подлый шпион. После того, как он попадает в производственный код, он точно все записывает. Вот как вы можете узнать, что делает ваш код и является ли он правильным.

Количество звонков

Самое простое — просто убедиться, что код вызывается ожидаемое количество раз. Атрибут .call_count— это именно то, что имеет значение.

def get_values(names, client):
    ret_value = []
    cache = {}
    for name in names:
        # name = name.lower()
        if name not in cache:
            value = client.get(f"https://httpbin.org/anything/grab?name={name}").json()['args']['name']
            cache[name] = value
        ret_value.append(cache[name])
    return ret_value

client = mock.MagicMock()
client.get.return_value.json.return_value = dict(args=dict(name="something"))
result = get_values(['one', 'One'], client)
print(result)
print("call count", client.get.call_count)

Результаты:

['something', 'something']
call count 2

Одним из преимуществ проверки .call_count >= 1по сравнению с проверкой .calledявляется то, что она более устойчива к глупым опечаткам.

def call_function(func):
    print("I'm going to call the function, really!")
    if False:
        func()
    print("I just called the function")

func = mock.MagicMock()
call_function(func)
print(func.callled) # TYPO -- Extra "l"
I'm going to call the function, really!
I just called the function
<MagicMock name='mock.callled' id='140228249343504'>

Усердное использование specможет предотвратить это. Однако specне является рекурсивным. Даже если исходный фиктивный объект имеет спецификацию, тест, который гарантирует, что каждый его атрибут также имеет спецификацию, встречается редко. Однако использование .call_countвместо .called— это простой хак, полностью исключающий возможность совершения этой ошибки.

Аргументы вызова

В следующем примере я гарантирую, что код вызывает метод с правильными аргументами. При автоматизации манипуляций с центром обработки данных важно все делать правильно. Как говорится: «Человеку свойственно ошибаться, а чтобы разрушить целый ЦОД, нужен робот с жуком».

Мы хотим убедиться, что наша автоматизация на основе Paramiko правильно получает размеры файлов, даже если в именах файлов есть пробелы.

def get_remote_file_size(client, fname):
    client.connect('ssh.example.com')
    stdin, stdout, stderr = client.exec_command(f"ls -l {fname}")
    stdin.close()
    results = stdout.read()
    errors = stderr.read()
    stdout.close()
    stderr.close()
    if errors != '':
        raise ValueError("problem with command", errors)
    return int(results.split()[4])

fname = "a file"
client = mock.MagicMock()
client.exec_command.return_value = [mock.MagicMock(name=str(i)) for i in range(3)]
client.exec_command.return_value[1].read.return_value = f"""\
-rw-rw-r--  1 user user    123 Jul 18 20:25 {fname}
"""
client.exec_command.return_value[2].read.return_value = ""
result = get_remote_file_size(client, fname)
assert result == 123
[args], kwargs = client.exec_command.call_args
import shlex
print(shlex.split(args))

Результаты:

['ls', '-l', 'a', 'file']

Упс! Это не правильная команда. Хорошо, что вы проверили аргументы.

Глубокое погружение в макеты

Моки обладают большой силой. Как и любой мощный инструмент, его неправильное использование — быстрый способ попасть в большой беспорядок. Но правильно используя .return_value, .side_effect, и различные .call*свойства, можно написать лучшие модульные тесты.

Хороший юнит-тест — это тот, который:

  • Сбой при наличии неправильного кода
  • Проходит при наличии правильного кода

«Качество» не бинарно. Он существует в спектре. Плохость модульного теста определяется :

  • Сколько ошибок он пропускает. Это «пропавший сигнал тревоги» или «ложноотрицательный результат». Если вы статистик, это «ошибка второго рода».
  • Сколько правильных изменений кода он терпит неудачу. Это «ложная тревога» или «ложное срабатывание». Если вы статистик, это «ошибки первого типа».

При использовании макета найдите время и подумайте об обеих метриках, чтобы оценить, поможет ли вам этот макет и этот модульный тест.

Оригинальный источник статьи: https://opensource.com/

#python #mock 

Как использовать макеты в Python
津田  淳

津田 淳

1680408120

如何在 Python 中使用模拟

使用模拟安全地测试您的代码。

4 月 1 日是关于假故事和假装的日子。这使它成为谈论模拟的完美日子。

有时,使用真实的对象是困难的、不明智的或复杂的。例如,arequests.Session连接到真实网站。在你的单元测试中使用它会带来很多……很多……问题。

Python 中的基本模拟

“模拟”是一个单元测试概念。他们生产的物品是真实物品的替代品。

from unittest import mock

整个家庭手工业都会解释“模拟”、“伪造”和“存根”之间的细微差别。在本文中,我交替使用这些术语。

regular = mock.MagicMock()

def do_something(o):
    return o.something(5)

do_something(regular)

此代码产生:

<MagicMock name='mock.something()' id='140228824621520'>

模拟具有所有方法。这些方法通常返回另一个 Mock。这可以通过将其分配给 来更改return_value

例如,假设您要调用以下函数:

def do_something(o):
    return o.something() + 1

它需要有.something()方法的东西。幸运的是,模拟对象拥有它:

obj = mock.MagicMock(name="an object")
obj.something.return_value = 2
print(do_something(obj))

答案:

3

也可以覆盖“魔术方法”:

a = mock.MagicMock()
a.__str__.return_value = "an a"
print(str(a))

答案:

an a

规范

使用规范确保模拟没有“额外”方法或属性。例如,这里有一些应该失败的代码:

import pathlib

def bad_pathlib_usage(path):
    ## TYPO: missing underscore
    path.writetext("hello")

dummy_path = mock.MagicMock(spec=pathlib.Path)

try:
    bad_pathlib_usage(dummy_path)
except Exception as exc:
    print("Failed!", repr(exc))

结果:

Failed! AttributeError("Mock object has no attribute 'writetext'")

模拟副作用

有时,MagicMock每次都返回相同的东西并不完全是您需要的。例如,sys.stdin.readline()通常返回不同的值,而不是在整个测试过程中返回相同的值。

该属性side_effect允许在比使用return_value.

可迭代的

可以分配给的事物之一side_effect是可迭代对象,例如序列或生成器。

这是一个强大的功能。它允许用很少的代码控制每个调用的返回值。

different_things = mock.MagicMock()
different_things.side_effect = [1, 2, 3]
print(different_things())
print(different_things())
print(different_things())

输出:

1
2
3

一个更现实的例子是在模拟文件输入时。在这种情况下,我希望能够控制readline每次返回的内容以假装它是文件输入:

def parse_three_lines(fpin):
    line = fpin.readline()
    name, value = line.split()
    modifier = fpin.readline().strip()
    extra = fpin.readline().strip()
    return {name: f"{value}/{modifier}+{extra}"}

from io import TextIOBase
    
filelike = mock.MagicMock(spec=TextIOBase)
filelike.readline.side_effect = [
    "thing important\n",
    "a-little\n",
    "to-some-people\n"
]
value = parse_three_lines(filelike)
print(value)

结果:

{'thing': 'important/a-little+to-some-people'}

例外

另一件可能的事情是为side_effect属性分配一个例外。这会导致调用引发您分配的异常。使用此功能可以模拟环境中的边缘条件,通常恰好是:

  • 你在乎
  • 很难真实地模拟

一种常见的情况是网络问题。根据墨菲定律,它们总是在凌晨 4 点发生,导致寻呼机响起,而当您坐在办公桌前时,它们绝不会发生在上午 10 点。下面是基于我编写的测试网络服务的真实代码。

在这个简化的示例中,代码返回响应行的长度,如果达到超时则返回负数。该数字根据协议协商达成的时间而有所不同。例如,这允许代码区分“连接超时”和“响应超时”。

针对真实服务器测试此代码很困难。服务器努力避免中断!您可以分叉服务器的 C 代码并添加一些混乱,或者您可以只使用side_effect和模拟:

import socket

def careful_reader(sock):
    sock.settimeout(5)
    try:
        sock.connect(("some.host", 8451))
    except socket.timeout:
        return -1
    try:
        sock.sendall(b"DO THING\n")
    except socket.timeout:
        return -2
    fpin = sock.makefile()
    try:
        line = fpin.readline()
    except socket.timeout:
        return -3
    return len(line.strip())

from io import TextIOBase
from unittest import mock

sock = mock.MagicMock(spec=socket.socket)
sock.connect.side_effect = socket.timeout("too long")
print(careful_reader(sock))

结果是失败,在这种情况下意味着测试成功:

-1

通过仔细的副作用,您可以获得每个返回值。例如:

sock = mock.MagicMock(spec=socket.socket)
sock.sendall.side_effect = socket.timeout("too long")
print(careful_reader(sock))

结果:

-2

可调用

前面的例子被简化了。真正的网络服务测试代码必须验证它得到的结果是否正确,以验证服务器是否正常工作。这意味着进行综合请求并寻找正确的结果。模拟对象必须模拟它。它必须对输入执行一些计算。

尝试在不执行任何计算的情况下测试此类代码是很困难的。这些测试往往过于不敏感或过于“古怪”。

  • 不敏感测试是在存在错误的情况下不会失败的测试。
  • flakey 测试有时会失败,即使代码是正确的。

在这里,我的代码不正确。insensitive 测试不会捕获它,而 flakey 测试即使修复也会失败!

import socket
import random

def yolo_reader(sock):
    sock.settimeout(5)
    sock.connect(("some.host", 8451))
    fpin = sock.makefile()
    order = [0, 1]
    random.shuffle(order)
    while order:
        if order.pop() == 0:
            sock.sendall(b"GET KEY\n")
            key = fpin.readline().strip()
        else:
            sock.sendall(b"GET VALUE\n")
            value = fpin.readline().strip()
    return {value: key} ## Woops bug, should be {key: value}

以下内容太“不敏感”,无法检测到错误:

sock = mock.MagicMock(spec=socket.socket)
sock.makefile.return_value.readline.return_value = "interesting\n"
assert yolo_reader(sock) == {"interesting": "interesting"}

有时,即使错误不存在,以下内容也太“古怪”了:

for i in range(10):
    sock = mock.MagicMock(spec=socket.socket)
    sock.makefile.return_value.readline.side_effect = ["key\n", "value\n"]
    if yolo_reader(sock) != {"key": "value"}:
        print(i, end=" ")

例如:

3 6 7 9 

从模拟对象获取结果的最后一个选项是将一个可调用对象分配给side_effect. 这调用side_effect简单地调用它。为什么不直接将可调用对象分配给属性呢?耐心点,我会在下一部分讲到!

在此示例中,我的可调用对象(只是一个函数)将 a 分配return_value给另一个对象的属性。这并不少见。我是在模拟环境,在真实环境中,戳一个东西往往会影响到其他东西。

sock = mock.MagicMock(spec=socket.socket)
def sendall(data):
    cmd, name = data.decode("ascii").split()
    if name == "KEY":
        sock.makefile.return_value.readline.return_value = "key\n"
    elif name == "VALUE":
        sock.makefile.return_value.readline.return_value = "value\n"
    else:
        raise ValueError("got bad command", name)
sock.sendall.side_effect = sendall
print(yolo_reader(sock), dict(key="value"))

结果:

{'value': 'key'} {'key': 'value'}

模拟调用参数:代码的 X 射线

在编写单元测试时,您“远离”代码,但试图深入了解它的行为方式。Mock 对象是你偷偷摸摸的间谍。进入生产代码后,它忠实地记录了一切。通过这种方式,您可以找到代码的作用以及它是否正确。

通话次数

最简单的事情就是确保代码被调用了预期的次数。该.call_count属性正是重要的。

def get_values(names, client):
    ret_value = []
    cache = {}
    for name in names:
        # name = name.lower()
        if name not in cache:
            value = client.get(f"https://httpbin.org/anything/grab?name={name}").json()['args']['name']
            cache[name] = value
        ret_value.append(cache[name])
    return ret_value

client = mock.MagicMock()
client.get.return_value.json.return_value = dict(args=dict(name="something"))
result = get_values(['one', 'One'], client)
print(result)
print("call count", client.get.call_count)

结果:

['something', 'something']
call count 2

.call_count >= 1与检查相比,检查的一个好处.called是它更能抵抗愚蠢的拼写错误。

def call_function(func):
    print("I'm going to call the function, really!")
    if False:
        func()
    print("I just called the function")

func = mock.MagicMock()
call_function(func)
print(func.callled) # TYPO -- Extra "l"
I'm going to call the function, really!
I just called the function
<MagicMock name='mock.callled' id='140228249343504'>

勤奋地使用spec可以防止这种情况。但是,spec不是递归的。即使原始模拟对象有规范,也很少有测试确保它具有的每个属性也有规范。但是,使用.call_countinstead of.called是一种简单的技巧,可以完全消除发生此错误的机会。

调用参数

在下一个示例中,我确保代码使用正确的参数调用方法。在自动化数据中心操作时,把事情做好很重要。正如他们所说,“犯错是人之常情,但摧毁整个数据中心需要一个有虫子的机器人。”

我们希望确保基于 Paramiko 的自动化能够正确获取文件的大小,即使文件名中包含空格也是如此。

def get_remote_file_size(client, fname):
    client.connect('ssh.example.com')
    stdin, stdout, stderr = client.exec_command(f"ls -l {fname}")
    stdin.close()
    results = stdout.read()
    errors = stderr.read()
    stdout.close()
    stderr.close()
    if errors != '':
        raise ValueError("problem with command", errors)
    return int(results.split()[4])

fname = "a file"
client = mock.MagicMock()
client.exec_command.return_value = [mock.MagicMock(name=str(i)) for i in range(3)]
client.exec_command.return_value[1].read.return_value = f"""\
-rw-rw-r--  1 user user    123 Jul 18 20:25 {fname}
"""
client.exec_command.return_value[2].read.return_value = ""
result = get_remote_file_size(client, fname)
assert result == 123
[args], kwargs = client.exec_command.call_args
import shlex
print(shlex.split(args))

结果:

['ls', '-l', 'a', 'file']

糟糕!那不是正确的命令。幸好你检查了参数。

深入模拟

模拟有很大的力量。与任何强大的工具一样,使用不当会很快陷入大混乱。但正确使用.return_value.side_effect和各种.call*属性,可以编写出最好的单元测试。

一个好的单元测试是这样的:

  • 存在错误代码时失败
  • 在存在正确代码的情况下通过

“质量”不是二元的。它存在于一个频谱上。单元测试的不良程度取决于:

  • 它允许通过多少错误。那是“漏报”或“漏报”。如果您是统计学家,这是“2 类错误”。
  • 它失败了多少正确的代码更改。这是“误报”或“误报”。如果您是统计学家,那就是“第一类错误”。

使用 mock 时,花点时间考虑这两个指标,以评估这个 mock 和这个单元测试是否会帮助或阻碍你。

文章原文出处:https: //opensource.com/

#python #mock 

如何在 Python 中使用模拟
Bongani  Ngema

Bongani Ngema

1680393600

How to Use Mocks in Python

Test your code safely with mocks.

April 1st is all about fake stories and pretending. This makes it the perfect day to talk about mocking.

Sometimes, using real objects is hard, ill-advised, or complicated. For example, a requests.Session connects to real websites. Using it in your unittests invites a…lot…of problems.

Basic mocking in Python

"Mocks" are a unittest concept. They produce objects that are substitutes for the real ones.

from unittest import mock

There's a whole cottage industry that will explain that "mock", "fake", and "stub" are all subtly different. In this article, I use the terms interchangeably.

regular = mock.MagicMock()

def do_something(o):
    return o.something(5)

do_something(regular)

This code produces:

<MagicMock name='mock.something()' id='140228824621520'>

Mocks have all the methods. The methods usually return another Mock. This can be changed by assigning it to return_value.

For example, suppose you want to call the following function:

def do_something(o):
    return o.something() + 1

It requires something which has the .something() method. Luckily, mock objects have it:

obj = mock.MagicMock(name="an object")
obj.something.return_value = 2
print(do_something(obj))

The answer:

3

It is also possible to override the "magic methods":

a = mock.MagicMock()
a.__str__.return_value = "an a"
print(str(a))

The answer:

an a

The spec

Make sure that a mock does not have "extra" methods or attributes by using a spec. For example, here's some code that should fail:

import pathlib

def bad_pathlib_usage(path):
    ## TYPO: missing underscore
    path.writetext("hello")

dummy_path = mock.MagicMock(spec=pathlib.Path)

try:
    bad_pathlib_usage(dummy_path)
except Exception as exc:
    print("Failed!", repr(exc))

The result:

Failed! AttributeError("Mock object has no attribute 'writetext'")

Mock side effect

Sometimes, having a MagicMock that returns the same thing every time isn't quite everything you need it to be. For example, sys.stdin.readline() usually returns different values, not the same value throughout the test.

The property side_effect allows controlling what a magic mock returns on a more detailed level than using return_value.

Iterable

One of the things that can be assigned to side_effect is an iterable, such as a sequence or a generator.

This is a powerful feature. It allows controlling each call's return value, with little code.

different_things = mock.MagicMock()
different_things.side_effect = [1, 2, 3]
print(different_things())
print(different_things())
print(different_things())

The output:

1
2
3

A more realistic example is when simulating file input. In this case, I want to be able to control what readline returns each time to pretend it's file input:

def parse_three_lines(fpin):
    line = fpin.readline()
    name, value = line.split()
    modifier = fpin.readline().strip()
    extra = fpin.readline().strip()
    return {name: f"{value}/{modifier}+{extra}"}

from io import TextIOBase
    
filelike = mock.MagicMock(spec=TextIOBase)
filelike.readline.side_effect = [
    "thing important\n",
    "a-little\n",
    "to-some-people\n"
]
value = parse_three_lines(filelike)
print(value)

The result:

{'thing': 'important/a-little+to-some-people'}

Exception

Another thing that's possible is assigning an exception to the side_effect attribute. This causes the call to raise the exception you assigned. Using this feature allows simulating edge conditions in the environment, usually precisely the ones that:

  • You care about
  • Are hard to simulate realistically

One popular case is network issues. As per Murphy's law, they always happen at 4 AM, causing a pager to go off, and never at 10 AM when you're sitting at your desk. The following is based on real code I wrote to test a network service.

In this simplified example, the code returns the length of the response line, or a negative number if a timeout has been reached. The number is different based on when in the protocol negotiation this has been reached. This allows the code to distinguish "connection timeout" from "response timeout", for example.

Testing this code against a real server is hard. Servers try hard to avoid outages! You could fork the server's C code and add some chaos or you can just use side_effect and mock:

import socket

def careful_reader(sock):
    sock.settimeout(5)
    try:
        sock.connect(("some.host", 8451))
    except socket.timeout:
        return -1
    try:
        sock.sendall(b"DO THING\n")
    except socket.timeout:
        return -2
    fpin = sock.makefile()
    try:
        line = fpin.readline()
    except socket.timeout:
        return -3
    return len(line.strip())

from io import TextIOBase
from unittest import mock

sock = mock.MagicMock(spec=socket.socket)
sock.connect.side_effect = socket.timeout("too long")
print(careful_reader(sock))

The result is a failure, which in this case means a successful test:

-1

With careful side effects, you can get to each of the return values. For example:

sock = mock.MagicMock(spec=socket.socket)
sock.sendall.side_effect = socket.timeout("too long")
print(careful_reader(sock))

The result:

-2

Callable

The previous example is simplified. Real network service test code must verify that the results it got were correct to validate that the server works correctly. This means doing a synthetic request and looking for a correct result. The mock object has to emulate that. It has to perform some computation on the inputs.

Trying to test such code without performing any computation is difficult. The tests tend to be too insensitive or too "flakey".

  • An insensitive test is one that does not fail in the presence of bugs.
  • A flakey test is one that sometimes fails, even when the code is correct.

Here, my code is incorrect. The insensitive test does not catch it, while the flakey test would fail even if it was fixed!

import socket
import random

def yolo_reader(sock):
    sock.settimeout(5)
    sock.connect(("some.host", 8451))
    fpin = sock.makefile()
    order = [0, 1]
    random.shuffle(order)
    while order:
        if order.pop() == 0:
            sock.sendall(b"GET KEY\n")
            key = fpin.readline().strip()
        else:
            sock.sendall(b"GET VALUE\n")
            value = fpin.readline().strip()
    return {value: key} ## Woops bug, should be {key: value}

The following would be too "insensitive", not detecting the bug:

sock = mock.MagicMock(spec=socket.socket)
sock.makefile.return_value.readline.return_value = "interesting\n"
assert yolo_reader(sock) == {"interesting": "interesting"}

The following would be too "flakey," detecting the bug even if it's not there, sometimes:

for i in range(10):
    sock = mock.MagicMock(spec=socket.socket)
    sock.makefile.return_value.readline.side_effect = ["key\n", "value\n"]
    if yolo_reader(sock) != {"key": "value"}:
        print(i, end=" ")

For example:

3 6 7 9 

The final option of getting results from a mock object is to assign a callable object to side_effect. This calls side_effect to simply call it. Why not just assign a callable object directly to the attribute? Have patience, I'll get to that in the next part!

In this example, my callable object (just a function) assigns a return_value to the attribute of another object. This isn't that uncommon. I'm simulating the environment, and in a real environment, poking one thing often affects other things.

sock = mock.MagicMock(spec=socket.socket)
def sendall(data):
    cmd, name = data.decode("ascii").split()
    if name == "KEY":
        sock.makefile.return_value.readline.return_value = "key\n"
    elif name == "VALUE":
        sock.makefile.return_value.readline.return_value = "value\n"
    else:
        raise ValueError("got bad command", name)
sock.sendall.side_effect = sendall
print(yolo_reader(sock), dict(key="value"))

The result:

{'value': 'key'} {'key': 'value'}

Mock call arguments: x-ray for code

When writing a unit test, you are "away" from the code but trying to peer into its guts to see how it behaves. The Mock object is your sneaky spy. After it gets into the production code, it records everything faithfully. This is how you can find what your code does and whether it's the right thing.

Call counts

The simplest thing is to just make sure that the code is called the expected number of times. The .call_count attribute is exactly what counts that.

def get_values(names, client):
    ret_value = []
    cache = {}
    for name in names:
        # name = name.lower()
        if name not in cache:
            value = client.get(f"https://httpbin.org/anything/grab?name={name}").json()['args']['name']
            cache[name] = value
        ret_value.append(cache[name])
    return ret_value

client = mock.MagicMock()
client.get.return_value.json.return_value = dict(args=dict(name="something"))
result = get_values(['one', 'One'], client)
print(result)
print("call count", client.get.call_count)

The results:

['something', 'something']
call count 2

One benefit of checking .call_count >= 1 as opposed to checking .called is that it is more resistant to silly typos.

def call_function(func):
    print("I'm going to call the function, really!")
    if False:
        func()
    print("I just called the function")

func = mock.MagicMock()
call_function(func)
print(func.callled) # TYPO -- Extra "l"
I'm going to call the function, really!
I just called the function
<MagicMock name='mock.callled' id='140228249343504'>

Using spec diligently can prevent that. However, spec is not recursive. Even if the original mock object has a spec, rare is the test that makes sure that every single attribute it has also has a spec. However, using .call_count instead of .called is a simple hack that completely eliminates the chance to make this error.

Call arguments

In the next example, I ensure the code calls the method with the correct arguments. When automating data center manipulations, it's important to get things right. As they say, "To err is human, but to destroy an entire data center requires a robot with a bug."

We want to make sure our Paramiko-based automation correctly gets the sizes of files, even when the file names have spaces in them.

def get_remote_file_size(client, fname):
    client.connect('ssh.example.com')
    stdin, stdout, stderr = client.exec_command(f"ls -l {fname}")
    stdin.close()
    results = stdout.read()
    errors = stderr.read()
    stdout.close()
    stderr.close()
    if errors != '':
        raise ValueError("problem with command", errors)
    return int(results.split()[4])

fname = "a file"
client = mock.MagicMock()
client.exec_command.return_value = [mock.MagicMock(name=str(i)) for i in range(3)]
client.exec_command.return_value[1].read.return_value = f"""\
-rw-rw-r--  1 user user    123 Jul 18 20:25 {fname}
"""
client.exec_command.return_value[2].read.return_value = ""
result = get_remote_file_size(client, fname)
assert result == 123
[args], kwargs = client.exec_command.call_args
import shlex
print(shlex.split(args))

The results:

['ls', '-l', 'a', 'file']

Woops! That's not the right command. Good thing you checked the arguments.

Deep dive into mocks

Mocks have a lot of power. Like any powerful tool, using it improperly is a fast way to get into a big mess. But properly using the .return_value, .side_effect, and the various .call* properties, it's possible to write the best sort of unit tests.

A good unit test is one that:

  • Fails in the presence of incorrect code
  • Passes in the presence of correct code

"Quality" is not binary. It exists on a spectrum. The badness of a unit test is determined by:

  • How many errors it lets pass. That's a "missing alarm" or "false negative". If you're a statistician, it's a "type 2 error".
  • How many correct code changes it fails. That's a "false alarm" or "false positive". If you're a statistician, it's a "type 1 errors".

When using a mock, take the time and think about both metrics to evaluate whether this mock and this unit test, will help or hinder you.

Original article source at: https://opensource.com/

#python #mock 

How to Use Mocks in Python
Rupert  Beatty

Rupert Beatty

1678251251

Mockolo: Efficient Mock Generator for Swift

Welcome to Mockolo

Mockolo is an efficient mock generator for Swift. Swift doesn't provide mocking support, and Mockolo provides a fast and easy way to autogenerate mock objects that can be tested in your code. One of the main objectives of Mockolo is fast performance. Unlike other frameworks, Mockolo provides highly performant and scalable generation of mocks via a lightweight commandline tool, so it can run as part of a linter or a build if one chooses to do so. Try Mockolo and enhance your project's test coverage in an effective, performant way.

Motivation

One of the main objectives of this project is high performance. There aren't many 3rd party tools that perform fast on a large codebase containing, for example, over 2M LoC or over 10K protocols. They take several hours and even with caching enabled take several minutes. Mockolo was built to make highly performant generation of mocks possible (in the magnitude of seconds) on such large codebase. It uses a minimal set of frameworks necessary (mentioned in the Used libraries section) to keep the code lean and efficient.

Another objective is to enable flexibility in using or overriding types if needed. This allows use of some of the features that require deeper analysis such as protocols with associated types to be simpler, more straightforward, and less fragile.

Disclaimer

This project may contain unstable APIs which may not be ready for general use. Support and/or new releases may be limited.

System Requirements

  • Swift 5.4 or later
  • Xcode 12.5 or later
  • macOS 11.0 or later
  • Support is included for the Swift Package Manager

Build / Install

Option 1: By Mint

$ mint install uber/mockolo
$ mint run uber/mockolo mockolo -h // see commandline input options below

Option 2: Homebrew

$ brew install mockolo

Option 3: Use the binary

Go to the Release tab and download/install the binary directly.

Option 4: Clone and build/run

$ git clone https://github.com/uber/mockolo.git
$ cd mockolo
$ swift build -c release
$ .build/release/mockolo -h  // see commandline input options below

To call mockolo from any location, copy the executable into a directory that is part of your PATH environment variable.

To check out a specific version,

$ git tag -l
$ git checkout [tag]

To use Xcode to build and run,

$ swift package generate-xcodeproj

Run

Mockolo is a commandline executable. To run it, pass in a list of the source file directories or file paths of a build target, and the destination filepath for the mock output. To see other arguments to the commandline, run mockolo --help.

./mockolo -s myDir -d ./OutputMocks.swift -x Images Strings

This parses all the source files in myDir directory, excluding any files ending with Images or Strings in the file name (e.g. MyImages.swift), and generates mocks to a file at OutputMocks.swift in the current directory.

Use --help to see the complete argument options.

./mockolo -h  // or --help

OVERVIEW: Mockolo: Swift mock generator.

USAGE: mockolo [<options>] --destination <destination>

OPTIONS:
  --allow-set-call-count  If set, generated *CallCount vars will be allowed to set manually.
  --annotation <annotation>
                          A custom annotation string used to indicate if a type should be mocked (default = @mockable). (default: @mockable)
  -j, --concurrency-limit <n>
                          Maximum number of threads to execute concurrently (default = number of cores on the running machine).
  --custom-imports <custom-imports>
                          If set, custom module imports (separated by a space) will be added to the final import statement list.
  --enable-args-history   Whether to enable args history for all functions (default = false). To enable history per function, use the 'history' keyword in the annotation argument.
  --disable-combine-default-values
                          Whether to disable generating Combine streams in mocks (default = false). Set this to true to control how your streams are created in your mocks.
  --exclude-imports <exclude-imports>
                          If set, listed modules (separated by a space) will be excluded from the import statements in the mock output.
  -x, --exclude-suffixes <exclude-suffixes>
                          List of filename suffix(es) without the file extensions to exclude from parsing (separated by a space).
  --header <header>       A custom header documentation to be added to the beginning of a generated mock file.
  -l, --logging-level <n> The logging level to use. Default is set to 0 (info only). Set 1 for verbose, 2 for warning, and 3 for error. (default: 0)
  --macro <macro>         If set, #if [macro] / #endif will be added to the generated mock file content to guard compilation.
  --mock-all              If set, it will mock all types (protocols and classes) with a mock annotation (default is set to false and only mocks protocols with a mock annotation).
  --mock-filelist <mock-filelist>
                          Path to a file containing a list of dependent files (separated by a new line) of modules this target depends on.
  --mock-final            If set, generated mock classes will have the 'final' attributes (default is set to false).
  -mocks, --mockfiles <mocks>
                          List of mock files (separated by a space) from modules this target depends on. If the --mock-filelist value exists, this will be ignored.
  -d, --destination <destination>
                          Output file path containing the generated Swift mock classes. If no value is given, the program will exit.
  -s, --sourcedirs <sourcedirs>
                          Paths to the directories containing source files to generate mocks for (separated by a space). If the --filelist or --sourcefiles values exist, they will be ignored.
  -f, --filelist <filelist>
                          Path to a file containing a list of source file paths (delimited by a new line). If the --sourcedirs value exists, this will be ignored.
  -srcs, --sourcefiles <srcs>
                          List of source files (separated by a space) to generate mocks for. If the --sourcedirs or --filelist value exists, this will be ignored.
  -i, --testable-imports <testable-imports>
                          If set, @testable import statements will be added for each module name in this list (separated by a space).
  --use-mock-observable   If set, a property wrapper will be used to mock RxSwift Observable variables (default is set to false).
  --use-template-func     If set, a common template function will be called from all functions in mock classes (default is set to false).
  -h, --help              Show help information.

Add MockoloFramework to your project

Option 1: SPM


dependencies: [
    .package(url: "https://github.com/uber/mockolo.git", from: "1.8.1"),
],
targets: [
    .target(name: "MyTarget", dependencies: ["MockoloFramework"]),
]

Option 2: Cocoapods

target 'MyTarget' do
  platform :osx, '10.14'
  pod 'MockoloFramework', '~>1.1.2'
end

Distribution

The install-script.sh will build and package up the mockolo binary and other necessary resources in the same bundle.

$ ./install-script.sh -h  // see input options
$ ./install-script.sh -s [source dir] -t mockolo -d [destination dir] -o [output filename].tar.gz

This will create a tarball for distribution, which contains the mockolo executable along with a necessary SwiftSyntax parser dylib (lib_InternalSwiftSyntaxParser.dylib). This allows running mockolo without depending on where the dylib lives.

How to use

For example, Foo.swift contains:

/// @mockable
public protocol Foo {
    var num: Int { get set }
    func bar(arg: Float) -> String
}

Running ./mockolo -srcs Foo.swift -d ./OutputMocks.swift will output:

public class FooMock: Foo {
    init() {}
    init(num: Int = 0) {
        self.num = num
    }

    var numSetCallCount = 0
    var underlyingNum: Int = 0
    var num: Int {
        get {
            return underlyingNum
        }
        set {
            underlyingNum = newValue
            numSetCallCount += 1
        }
    }

    var barCallCount = 0
    var barHandler: ((Float) -> (String))?
    func bar(arg: Float) -> String {
        barCallCount += 1
        if let barHandler = barHandler {
            return barHandler(arg)
        }
        return ""
    }
}

The above mock can now be used in a test as follows:

func testMock() {
    let mock = FooMock(num: 5)
    XCTAssertEqual(mock.numSetCallCount, 1)
    mock.barHandler = { arg in
        return String(arg)
    }
    XCTAssertEqual(mock.barCallCount, 1)
}

Arguments

A list of override arguments can be passed in (delimited by a semicolon) to the annotation to set var types, typealiases, module names, etc.

Module

/// @mockable(module: prefix = Bar)
public protocol Foo {
    ...
}

This will generate:

public class FooMock: Bar.Foo {
    ...
}

Typealias

/// @mockable(typealias: T = AnyObject; U = StringProtocol)
public protocol Foo {
    associatedtype T
    associatedtype U: Collection where U.Element == T
    associatedtype W
    ...
}

This will generate the following mock output:

public class FooMock: Foo {
    typealias T = AnyObject // overriden
    typealias U = StringProtocol // overriden
    typealias W = Any // default placeholder type for typealias
    ...
}

RxSwift

For a var type such as an RxSwift observable:

/// @mockable(rx: intStream = ReplaySubject; doubleStream = BehaviorSubject)
public protocol Foo {
    var intStream: Observable<Int> { get }
    var doubleStream: Observable<Double> { get }
}

This will generate:

public class FooMock: Foo {
    var intStreamSubject = ReplaySubject<Int>.create(bufferSize: 1)
    var intStream: Observable<Int> { /* use intStreamSubject */ }
    var doubleStreamSubject = BehaviorSubject<Int>(value: 0)
    var doubleStream: Observable<Int> { /* use doubleStreamSubject */ }
}

Function Argument History

To capture function arguments history:

/// @mockable(history: fooFunc = true)
public protocol Foo {
    func fooFunc(val: Int)
    func barFunc(_ val: (a: String, Float))
    func bazFunc(val1: Int, val2: String)
}

This will generate:

public class FooMock: Foo {
    var fooFuncCallCount = 0
    var fooFuncArgValues = [Int]() // arguments captor
    var fooFuncHandler: ((Int) -> ())?
    func fooFunc(val: Int) {
        fooFuncCallCount += 1
        fooFuncArgValues.append(val)   // capture arguments

        if fooFuncHandler = fooFuncHandler {
            fooFuncHandler(val)
        }
    }

    ...
    var barFuncArgValues = [(a: String, Float)]() // tuple is also supported.
    ...

    ...
    var bazFuncArgValues = [(Int, String)]()
    ...
}

and also, enable the arguments captor for all functions if you passed --enable-args-history arg to mockolo command.

NOTE: The arguments captor only supports singular types (e.g. variable, tuple). The closure variable is not supported.

Combine's AnyPublisher

To generate mocks for Combine's AnyPublisher:

/// @mockable(combine: fooPublisher = PassthroughSubject; barPublisher = CurrentValueSubject)
public protocol Foo {
    var fooPublisher: AnyPublisher<String, Never> { get }
    var barPublisher: AnyPublisher<Int, CustomError> { get }
}

This will generate:

public class FooMock: Foo {
    public init() { }

    public var fooPublisher: AnyPublisher<String, Never> { return self.fooPublisherSubject.eraseToAnyPublisher() }
    public private(set) var fooPublisherSubject = PassthroughSubject<String, Never>()

    public var barPublisher: AnyPublisher<Int, CustomError> { return self.barPublisherSubject.eraseToAnyPublisher() }
    public private(set) var barPublisherSubject = CurrentValueSubject<Int, CustomError>(0)
}

You can also connect an AnyPublisher to a property within the protocol.

For example:

/// @mockable(combine: fooPublisher = @Published foo)
public protocol Foo {
    var foo: String { get }
    var fooPublisher: AnyPublisher<String, Never> { get }
}

This will generate:

public class FooMock: Foo {
    public init() { }
    public init(foo: String = "") {
        self.foo = foo
    }

    public private(set) var fooSetCallCount = 0
    @Published public var foo: String = "" { didSet { fooSetCallCount += 1 } }

    public var fooPublisher: AnyPublisher<String, Never> { return self.$foo.setFailureType(to: Never.self).eraseToAnyPublisher() }
}

Overrides

To override the generated mock name:

/// @mockable(override: name = FooMock)
public protocol FooProtocol { ... }

This will generate:

public class FooMock: FooProtocol { ... }

Used libraries

SwiftSyntax |

How to contribute to Mockolo

See CONTRIBUTING for more info.

Report any issues

If you run into any problems, please file a git issue. Please include:

  • The OS version (e.g. macOS 10.14.6)
  • The Swift version installed on your machine (from swift --version)
  • The Xcode version
  • The specific release version of this source code (you can use git tag to get a list of all the release versions or git log to get a specific commit sha)
  • Any local changes on your machine

Download Details:

Author: uber
Source Code: https://github.com/uber/mockolo 
License: Apache-2.0 license

#swift #mock #commandline #xcode 

Mockolo: Efficient Mock Generator for Swift
Lawrence  Lesch

Lawrence Lesch

1677755640

Smocker: A Simple and Efficient HTTP Mock Server and Proxy

Smocker (server mock) is a simple and efficient HTTP mock server.


Installation

With Docker

docker run -d \
  --restart=always \
  -p 8080:8080 \
  -p 8081:8081 \
  --name smocker \
  thiht/smocker

Manual Deployment

# This will be the deployment folder for the Smocker instance
mkdir -p /opt/smocker && cd /opt/smocker
wget -P /tmp https://github.com/Thiht/smocker/releases/latest/download/smocker.tar.gz
tar xf /tmp/smocker.tar.gz
nohup ./smocker -mock-server-listen-port=8080 -config-listen-port=8081 &

Healthcheck

curl localhost:8081/version

User Interface

Smocker exposes a configuration user interface. You can access it in your web browser on http://localhost:8081/.

History

Mocks

Usage

Smocker exposes two ports:

  • 8080 is the mock server port. It will expose the routes you register through the configuration port
  • 8081 is the configuration port. It's the port you will use to register new mocks. This port also exposes a user interface.

Hello, World!

To register a mock, you can use the YAML and the JSON formats. A basic mock might look like this:

# helloworld.yml
# This mock register two routes: GET /hello/world and GET /foo/bar
- request:
    # Note: the method could be omitted because GET is the default
    method: GET
    path: /hello/world
  response:
    # Note: the status could be omitted because 200 is the default
    status: 200
    headers:
      Content-Type: application/json
    body: >
      {
        "hello": "Hello, World!"
      }

- request:
    method: GET
    path: /foo/bar
  response:
    status: 204

You can then register it to the configuration server with the following command:

curl -XPOST \
  --header "Content-Type: application/x-yaml" \
  --data-binary "@helloworld.yml" \
  localhost:8081/mocks

After your mock is registered, you can query the mock server on the specified route, so that it returns the expected response to you:

$ curl -i localhost:8080/hello/world
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 05 Sep 2019 15:49:32 GMT
Content-Length: 31

{
  "hello": "Hello, World!"
}

To cleanup the mock server without restarting it, you can execute the following command:

curl -XPOST localhost:8081/reset

For more advanced usage, please read the project's documentation.

Development

Backend

The backend is written in Go. You can use the following commands to manage the development lifecycle:

  • make start: start the backend in development mode, with live reload
  • make build, make VERSION=xxx build: compile the code and generate a binary
  • make lint: run static analysis on the code
  • make format: automatically format the backend code
  • make test: execute unit tests
  • make test-integration: execute integration tests

Frontend

The frontend is written with TypeScript and React. You can use the following commands to manage the development lifecycle:

  • yarn install: install the dependencies
  • yarn start: start the frontend in development mode, with live reload
  • yarn build: generate the transpiled and minified files and assets
  • yarn lint: run static analysis on the code
  • yarn format: automatically format the frontend code
  • yarn test: execute unit tests
  • yarn test:watch: execute unit tests, with live reload

Documentation

The documentation is written in Markdown using Vuepress. You can use the following commands to manage the documentation:

  • yarn install: install the dependencies
  • yarn docs:generate: regenerate documentation screenshots (require the whole application to be started on the default ports)
  • yarn docs:dev: start the documentation in development mode, with live reload
  • yarn docs:build: generate the static production documentation

Docker

The application can be packaged as a standalone Docker image. You can use the following commands to manage the development lifecycle:

  • make build-docker, make VERSION=xxx build-docker: build the application as a Docker image
  • make start-docker, make VERSION=xxx start-docker: run a Smocker Docker image

Caddy

If you need to test Smocker with a base path, you can use the Caddyfile provided in the repository (Caddy v2):

  • make start-release, make VERSION=xxx start-release: create a released version of Smocker and launch it with /smocker/ as base path
  • make start-caddy: start Caddy to make Smocker accessible at http://localhost:8082/smocker/

HTTPS

If you need to test Smocker with HTTPS enabled, the easiest way is to generate a locally signed certificate with mkcert:

# Install the local certificate authority
mkcert -install

# Create a certificate for localhost
mkcert -cert-file /tmp/cert.pem -key-file /tmp/key.pem localhost

Then, start Smocker with TLS enabled, using your generated certificate:

./smocker -mock-server-listen-port=44300 -config-listen-port=44301 -tls-enable -tls-cert-file=/tmp/cert.pem -tls-private-key-file=/tmp/key.pm

The documentation is available on smocker.dev.


Contributors


Download Details:

Author: Thiht
Source Code: https://github.com/Thiht/smocker 
License: MIT license

#typescript #react #go #mock #api 

Smocker: A Simple and Efficient HTTP Mock Server and Proxy
Lawrence  Lesch

Lawrence Lesch

1677668905

TS-mockito: Mocking Library for TypeScript

TS-mockito

Mocking library for TypeScript inspired by http://mockito.org/

1.x to 2.x migration guide

1.x to 2.x migration guide

Main features

  • Strongly typed
  • IDE autocomplete
  • Mock creation (mock) (also abstract classes) #example
  • Spying on real objects (spy) #example
  • Changing mock behavior (when) via:
  • Checking if methods were called with given arguments (verify)
    • anything, notNull, anyString, anyOfClass etc. - for more flexible comparision
    • once, twice, times, atLeast etc. - allows call count verification #example
    • calledBefore, calledAfter - allows call order verification #example
  • Resetting mock (reset, resetCalls) #example, #example
  • Capturing arguments passed to method (capture) #example
  • Recording multiple behaviors #example
  • Readable error messages (ex. 'Expected "convertNumberToString(strictEqual(3))" to be called 2 time(s). But has been called 1 time(s).')

Installation

npm install ts-mockito --save-dev

Usage

Basics

// Creating mock
let mockedFoo:Foo = mock(Foo);

// Getting instance from mock
let foo:Foo = instance(mockedFoo);

// Using instance in source code
foo.getBar(3);
foo.getBar(5);

// Explicit, readable verification
verify(mockedFoo.getBar(3)).called();
verify(mockedFoo.getBar(anything())).called();

Stubbing method calls

// Creating mock
let mockedFoo:Foo = mock(Foo);

// stub method before execution
when(mockedFoo.getBar(3)).thenReturn('three');

// Getting instance
let foo:Foo = instance(mockedFoo);

// prints three
console.log(foo.getBar(3));

// prints null, because "getBar(999)" was not stubbed
console.log(foo.getBar(999));

Stubbing getter value

// Creating mock
let mockedFoo:Foo = mock(Foo);

// stub getter before execution
when(mockedFoo.sampleGetter).thenReturn('three');

// Getting instance
let foo:Foo = instance(mockedFoo);

// prints three
console.log(foo.sampleGetter);

Stubbing property values that have no getters

Syntax is the same as with getter values.

Please note, that stubbing properties that don't have getters only works if Proxy object is available (ES6).

Call count verification

// Creating mock
let mockedFoo:Foo = mock(Foo);

// Getting instance
let foo:Foo = instance(mockedFoo);

// Some calls
foo.getBar(1);
foo.getBar(2);
foo.getBar(2);
foo.getBar(3);

// Call count verification
verify(mockedFoo.getBar(1)).once();               // was called with arg === 1 only once
verify(mockedFoo.getBar(2)).twice();              // was called with arg === 2 exactly two times
verify(mockedFoo.getBar(between(2, 3))).thrice(); // was called with arg between 2-3 exactly three times
verify(mockedFoo.getBar(anyNumber()).times(4);    // was called with any number arg exactly four times
verify(mockedFoo.getBar(2)).atLeast(2);           // was called with arg === 2 min two times
verify(mockedFoo.getBar(anything())).atMost(4);   // was called with any argument max four times
verify(mockedFoo.getBar(4)).never();              // was never called with arg === 4

Call order verification

// Creating mock
let mockedFoo:Foo = mock(Foo);
let mockedBar:Bar = mock(Bar);

// Getting instance
let foo:Foo = instance(mockedFoo);
let bar:Bar = instance(mockedBar);

// Some calls
foo.getBar(1);
bar.getFoo(2);

// Call order verification
verify(mockedFoo.getBar(1)).calledBefore(mockedBar.getFoo(2));    // foo.getBar(1) has been called before bar.getFoo(2)
verify(mockedBar.getFoo(2)).calledAfter(mockedFoo.getBar(1));    // bar.getFoo(2) has been called before foo.getBar(1)
verify(mockedFoo.getBar(1)).calledBefore(mockedBar.getFoo(999999));    // throws error (mockedBar.getFoo(999999) has never been called)

Throwing errors

let mockedFoo:Foo = mock(Foo);

when(mockedFoo.getBar(10)).thenThrow(new Error('fatal error'));

let foo:Foo = instance(mockedFoo);
try {
    foo.getBar(10);
} catch (error:Error) {
    console.log(error.message); // 'fatal error'
}

Custom function

You can also stub method with your own implementation

let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);

when(mockedFoo.sumTwoNumbers(anyNumber(), anyNumber())).thenCall((arg1:number, arg2:number) => {
    return arg1 * arg2; 
});

// prints '50' because we've changed sum method implementation to multiply!
console.log(foo.sumTwoNumbers(5, 10));

Resolving / rejecting promises

You can also stub method to resolve / reject promise

let mockedFoo:Foo = mock(Foo);

when(mockedFoo.fetchData("a")).thenResolve({id: "a", value: "Hello world"});
when(mockedFoo.fetchData("b")).thenReject(new Error("b does not exist"));

Resetting mock calls

You can reset just mock call counter

// Creating mock
let mockedFoo:Foo = mock(Foo);

// Getting instance
let foo:Foo = instance(mockedFoo);

// Some calls
foo.getBar(1);
foo.getBar(1);
verify(mockedFoo.getBar(1)).twice();      // getBar with arg "1" has been called twice

// Reset mock
resetCalls(mockedFoo);

// Call count verification
verify(mockedFoo.getBar(1)).never();      // has never been called after reset

You can also reset calls of multiple mocks at once resetCalls(firstMock, secondMock, thirdMock)

Resetting mock

Or reset mock call counter with all stubs

// Creating mock
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(1)).thenReturn("one").

// Getting instance
let foo:Foo = instance(mockedFoo);

// Some calls
console.log(foo.getBar(1));               // "one" - as defined in stub
console.log(foo.getBar(1));               // "one" - as defined in stub
verify(mockedFoo.getBar(1)).twice();      // getBar with arg "1" has been called twice

// Reset mock
reset(mockedFoo);

// Call count verification
verify(mockedFoo.getBar(1)).never();      // has never been called after reset
console.log(foo.getBar(1));               // null - previously added stub has been removed

You can also reset multiple mocks at once reset(firstMock, secondMock, thirdMock)

Capturing method arguments

let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);

// Call method
foo.sumTwoNumbers(1, 2);

// Check first arg captor values
const [firstArg, secondArg] = capture(mockedFoo.sumTwoNumbers).last();
console.log(firstArg);    // prints 1
console.log(secondArg);    // prints 2

You can also get other calls using first(), second(), byCallIndex(3) and more...

Recording multiple behaviors

You can set multiple returning values for same matching values

const mockedFoo:Foo = mock(Foo);

when(mockedFoo.getBar(anyNumber())).thenReturn('one').thenReturn('two').thenReturn('three');

const foo:Foo = instance(mockedFoo);

console.log(foo.getBar(1));    // one
console.log(foo.getBar(1));    // two
console.log(foo.getBar(1));    // three
console.log(foo.getBar(1));    // three - last defined behavior will be repeated infinitely

Another example with specific values

let mockedFoo:Foo = mock(Foo);

when(mockedFoo.getBar(1)).thenReturn('one').thenReturn('another one');
when(mockedFoo.getBar(2)).thenReturn('two');

let foo:Foo = instance(mockedFoo);

console.log(foo.getBar(1));    // one
console.log(foo.getBar(2));    // two
console.log(foo.getBar(1));    // another one
console.log(foo.getBar(1));    // another one - this is last defined behavior for arg '1' so it will be repeated
console.log(foo.getBar(2));    // two
console.log(foo.getBar(2));    // two - this is last defined behavior for arg '2' so it will be repeated

Short notation:

const mockedFoo:Foo = mock(Foo);

// You can specify return values as multiple thenReturn args
when(mockedFoo.getBar(anyNumber())).thenReturn('one', 'two', 'three');

const foo:Foo = instance(mockedFoo);

console.log(foo.getBar(1));    // one
console.log(foo.getBar(1));    // two
console.log(foo.getBar(1));    // three
console.log(foo.getBar(1));    // three - last defined behavior will be repeated infinity

Possible errors:

const mockedFoo:Foo = mock(Foo);

// When multiple matchers, matches same result:
when(mockedFoo.getBar(anyNumber())).thenReturn('one');
when(mockedFoo.getBar(3)).thenReturn('one');

const foo:Foo = instance(mockedFoo);
foo.getBar(3); // MultipleMatchersMatchSameStubError will be thrown, two matchers match same method call

Mocking interfaces

You can mock interfaces too, just instead of passing type to mock function, set mock function generic type Mocking interfaces requires Proxy implementation

let mockedFoo:Foo = mock<FooInterface>(); // instead of mock(FooInterface)
const foo: SampleGeneric<FooInterface> = instance(mockedFoo);

Mocking types

You can mock abstract classes

const mockedFoo: SampleAbstractClass = mock(SampleAbstractClass);
const foo: SampleAbstractClass = instance(mockedFoo);

You can also mock generic classes, but note that generic type is just needed by mock type definition

const mockedFoo: SampleGeneric<SampleInterface> = mock(SampleGeneric);
const foo: SampleGeneric<SampleInterface> = instance(mockedFoo);

Spying on real objects

You can partially mock an existing instance:

const foo: Foo = new Foo();
const spiedFoo = spy(foo);

when(spiedFoo.getBar(3)).thenReturn('one');

console.log(foo.getBar(3)); // 'one'
console.log(foo.getBaz()); // call to a real method

You can spy on plain objects too:

const foo = { bar: () => 42 };
const spiedFoo = spy(foo);

foo.bar();

console.log(capture(spiedFoo.bar).last()); // [42] 

Thanks


Download Details:

Author: NagRock
Source Code: https://github.com/NagRock/ts-mockito 
License: MIT license

#typescript #testing #mock 

TS-mockito: Mocking Library for TypeScript
Rupert  Beatty

Rupert Beatty

1673405220

SwiftyMocky: Framework for Automatic Mock Generation

Overview

SwiftyMocky is a strongly typed framework for Mockito-like unit testing experience. Library depends on Sourcery, that scans your source code and generates Mocks Swift code for you!

The idea of SwiftyMocky is to automatically mock Swift protocols and protocol compositions. The main features are:

  • easy syntax, utilising full power of auto-complete, which makes writing test easier and faster
  • we DO support generics
  • mock implementations generation
  • a way to specify what mock will return (given)
  • possibility to specify different return values for different attributes
  • record stubbed return values sequence
  • verify, whether a method was called on mock or not
  • check method invocations with specified attributes
  • it works with real device

 

Important!!! Version 4.1.x

CLI was moved bask to the main (this) repo. CLI in this repository will be supported at least until version 5.0.0.

Version 4.0.x

Current version has several significant changes. It removes deprecated methods (which might be breaking) and deprecates having CLI in the new repository.

SwiftyPrototype was also extracted to separate library. There are no more compilation flags, so if you were relying on SwiftyMocky with -DMockyCustom, you will have to switch to SwiftyPrototype.

We consider current version as stable. We are moving toward using the new Mockfile but the previous configuration format would be still supported. Library works with Swift 4.1, 4.2, 5.0, 5.1.2 and Sourcery 1.0.x.

While it is technically possible to integrate SwiftyMocky on Linux targets, there is no Mock generation feature there yet. You can use SwiftyMokcy runtime via SwiftPM though, as long as your are fine with generating mocks on mac machine.

Migration from 3.2.0 and below

The migration is not required, you can keep using SwiftyMocky as you did before. The Legacy setup is described in guides section.

Still, we would encourage to try new CLI and share a feedback. We believe it will make using and setting up SwiftyMocky way easier. If you have an existing setup, install CLI as per this guide and try:

> swiftymocky migrate

 

Getting started

 

1. Integrating SwiftyMocky:

CocoaPods:

SwiftyMocky is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "SwiftyMocky"

Use CLI tool from your project directory:

# To setup initial Mockfile
% ./Pods/SwiftyMocky/bin/swiftymocky setup
# To generate mocks
% ./Pods/SwiftyMocky/bin/swiftymocky generate

Carthage:

To install, add following to you Cartfile:

github "MakeAWishFoundation/SwiftyMocky"

Then execute carthage update

For Carthage, few additional steps are required ⚠️. For detailed install instructions, see full documentation or consult Carthage documentation.

You need to install CLI to generate mocks - see installation

Swift Package Manager:

Add SwiftyMocky to you Package.swift dependencies:

dependencies: [
    .package(url: "https://github.com/MakeAWishFoundation/SwiftyMocky", from: "4.2.0"),
]

You need to install CLI to generate mocks - see installation

Note: Examples of SwiftyMocky integration as a tool for Unit tests, as well as a Prototyping framework, are here: https://github.com/MakeAWishFoundation/SM-Integration-Tests

 

2. Installing SwiftyMocky CLI:

Mint 🌱:

> brew install mint
> mint install MakeAWishFoundation/SwiftyMocky

Marathon 🏃:

> marathon install MakeAWishFoundation/SwiftyMocky

Make:

Clone from https://github.com/MakeAWishFoundation/SwiftyMockyCLI and run make in the root directory.


 

3. Generate mocks

Annotate your protocols that are going to be mocked, making them adopt AutoMockable protocol, or adding annotation comment above their definition in the source code.

Mocks are generated from your project root directory, based on configuration inside Mockfile.

> path/to/swiftymocky setup     # if you don't have a Mockfile yet
> path/to/swiftymocky doctor    # validate your setup
> path/to/swiftymocky generate  # generate mocks

More informations about CLI and mock generation

If you don't want to migrate to our CLI and prefer to use "raw" Sourcery, please refer to this section in documentation.

 

Usage

 

1. Marking protocols to be mocked

Create 'dummy' protocol somewhere in your project, like: protocol AutoMockable { }

Adopt it by every protocol you want to actually mock.

protocol ToBeMocked: AutoMockable {
  // ...
}

Alternatively, mark protocols that are meant to be mocked with sourcery annotation as following:

//sourcery: AutoMockable
protocol ToBeMocked {
  // ...
}

Or use it to protocol compositions:

typealias ToBeMocked = OneProtocol & TwoProtocols & AutoMockable

Every protocol in source directories, having this annotation, will be added to Mock.generated.swift

 

2. Stubbing return values for mock methods - Given

All mocks has given method (accessible both as instance method or global function), with easy to use syntax, allowing to specify what should be return values for given methods (based on specified attributes).

Generating mock

All protocol methods are nicely put into Given, with matching signature. That allows to use auto-complete (just type .) to see all mocked protocol methods, and specify return value for them.

All method attributes are wrapped as Parameter enum, allowing to choose between any and value, giving great flexibility to mock behaviour. Please consider following:

Given(mock, .surname(for name: .value("Johnny"), willReturn: "Bravo"))
Given(mock, .surname(for name: .any, willReturn: "Kowalsky"))

print(mock.surname(for: "Johny"))   // Bravo
print(mock.surname(for: "Mathew"))  // Kowalsky
print(mock.surname(for: "Joanna"))  // Kowalsky

In verions 3.0 we introduced sequences and policies for better control of mock behvaiour.

Given(mock, .surname(for name: .any, willReturn: "Bravo", "Kowalsky", "Nguyen"))

print(mock.surname(for: "Johny"))   // Bravo
print(mock.surname(for: "Johny"))   // Kowalsky
print(mock.surname(for: "Johny"))   // Nguyen
print(mock.surname(for: "Johny"))   // and again Bravo
// ...

For more details please see full documentation.

 

3. Check invocations of methods, subscripts and properties - Verify

All mocks has verify method (accessible both as instance method or global function), with easy to use syntax, allowing to verify, whether a method was called on mock, and how many times. It also provides convenient way to specify, whether method attributes matters (and which ones).

Generating mock

All protocol methods are nicely put into Verify, with matching signature. That allows to use auto-complete (just type .) to see all mocked protocol methods, and specify which one we want to verify.

All method attributes are wrapped as Parameter enum, allowing to choose between any, value and matching, giving great flexibility to tests. Please consider following:

// inject mock to sut. Every time sut saves user data, it should trigger storage storeUser method
sut.usersStorage = mockStorage
sut.saveUser(name: "Johny", surname: "Bravo")
sut.saveUser(name: "Johny", surname: "Cage")
sut.saveUser(name: "Jon", surname: "Snow")

// check if Jon Snow was stored at least one time
Verify(mockStorage, .storeUser(name: .value("Jon"), surname: .value("Snow")))
// storeUser method should be triggered 3 times in total, regardless of attributes values
Verify(mockStorage, 3, .storeUser(name: .any, surname: .any))
// storeUser method should be triggered 2 times with name Johny
Verify(mockStorage, 2, .storeUser(name: .value("Johny"), surname: .any))
// storeUser method should be triggered at least 2 times with name longer than 3
Verify(mockStorage, .moreOrEqual(to: 2), .storeUser(name: .matching({ $0.count > 3 }), surname: .any))

For Verify, you can use Count to specify how many times you expect something to be triggered. Count can be defined as explicit value, like 1,2,... or in more descriptive and flexible way, like .never, more(than: 1), etc.

From SwiftyMocky 3.0, it is possible to use Given and perform Verify on properties as well, with respect to whether it is get or set:

mock.name = "Danny"
mock.name = "Joanna"

print(mock.name)

// Verify getter:
Verify(mock, 1, .name)
// Verify setter:
Verify(mock, 2, .name(set: .any))
Verify(mock, 1, .name(set: .value("Danny")))
Verify(mock, .never, .name(set: .value("Bishop")))

 

4. Take action when a stubbed method is called - Perform

All mocks has perform method (accessible both as instance method or global function), with easy to use syntax, allowing to specify closure, that will be executed upon stubbed method being called.

It uses same parameter wrapping features as given, so you can specify different Perform cases for different attributes set.

It's very handy when working with completion block based approach.

Example:

// Perform allows to execute given closure, with all the method parameters, as soon as it is being called
Perform(mock, .methodThatTakesCompletionBlock(completion: .any, perform: { completion in
    completion(true,nil)
}))

 

Documentation

Full documentation is available here, as well as through docs directory.

Guides - Table of contents

Changelog is available here

 

All supported features

For list all supported features, check documentation here or guides

 

Examples of usage

For more examples, check out our example project, or examples section in guides.

To run the example project, clone the repo, and run pod install from the Example directory first.

To trigger mocks generation, run rake mock or swiftymocky generate from root directory (if you installed CLI).

 

Roadmap

  •  stubbing protocols in elegant way
  •  template for generating mocks
  •  example project
  •  stubbing protocols with variables
  •  method signature generation without name conflicts
  •  cover 95% of framework codebase with unit tests
  •  cover 95% of framework codebase with documentation
  •  add unit tests for template
  •  support for tvOS, Linux and MacOS
  •  Carthage support
  •  Subscripts support
  •  Stub return values as sequences
  •  Simple tool simplifying configuration process

Check out guides, or full documentation

Download Details:

Author: MakeAWishFoundation
Source Code: https://github.com/MakeAWishFoundation/SwiftyMocky 
License: MIT license

#swift #mock #unittest 

SwiftyMocky: Framework for Automatic Mock Generation
Hermann  Frami

Hermann Frami

1668056940

Stacks-blockchain: The Stacks Blockchain Implementation

Stacks 2.0

Reference implementation of the Stacks blockchain in Rust.

Stacks 2.0 is a layer-1 blockchain that connects to Bitcoin for security and enables decentralized apps and predictable smart contracts. Stacks 2.0 implements Proof of Transfer (PoX) mining that anchors to Bitcoin security. Leader election happens at the Bitcoin blockchain and Stacks (STX) miners write new blocks on the separate Stacks blockchain. With PoX there is no need to modify Bitcoin to enable smart contracts and apps around it. See this page for more details and resources.

Repository

Blockstack Topic/TechWhere to learn more
Stacks 2.0master branch
Stacks 1.0legacy branch
Use the packageour core docs
Develop a Blockstack Appour developer docs
Use a Blockstack Appour browser docs
Blockstack PBC the companyour website

Release Schedule and Hotfixes

Normal releases in this repository that add features such as improved RPC endpoints, improved boot-up time, new event observer fields or event types, etc., are released on a monthly schedule. The currently staged changes for such releases are in the develop branch. It is generally safe to run a stacks-node from that branch, though it has received less rigorous testing than release tags. If bugs are found in the develop branch, please do report them as issues on this repository.

For fixes that impact the correct functioning or liveness of the network, hotfixes may be issued. These are patches to the main branch which are backported to the develop branch after merging. These hotfixes are categorized by priority according to the following rubric:

  • High Priority. Any fix for an issue that could deny service to the network as a whole, e.g., an issue where a particular kind of invalid transaction would cause nodes to stop processing requests or shut down unintentionally. Any fix for an issue that could cause honest miners to produce invalid blocks.
  • Medium Priority. Any fix for an issue that could cause miners to waste funds.
  • Low Priority. Any fix for an issue that could deny service to individual nodes.

Versioning

This repository uses a 5 part version number.

X.Y.Z.A.n

X = 2 and does not change in practice unless there’s another Stacks 2.0 type event
Y increments on consensus-breaking changes
Z increments on non-consensus-breaking changes that require a fresh chainstate (akin to semantic MAJOR)
A increments on non-consensus-breaking changes that do not require a fresh chainstate, but introduce new features (akin to semantic MINOR)
n increments on patches and hot-fixes (akin to semantic PATCH)

For example, a node operator running version 2.0.10.0.0 would not need to wipe and refresh their chainstate to upgrade to 2.0.10.1.0 or 2.0.10.0.1. However, upgrading to 2.0.11.0.0 would require a new chainstate.

Roadmap

Stacks improvement proposals (SIPs) are aimed at describing the implementation of the Stacks blockchain, as well as proposing improvements. They should contain concise technical specifications of features or standards and the rationale behind it. SIPs are intended to be the primary medium for proposing new features, for collecting community input on a system-wide issue, and for documenting design decisions.

See SIP 000 for more details.

The SIPs are now located in the stacksgov/sips repository as part of the Stacks Community Governance organization.

Testnet versions

 Krypton is a Stacks 2 testnet with a fixed, two-minute block time, called regtest. Regtest is generally unstable for regular use, and is reset often. See the regtest documentation for more information on using regtest.

 Xenon is the Stacks 2 public testnet, which runs PoX against the Bitcoin testnet. It is the full implementation of the Stacks 2 blockchain, and should be considered a stable testnet for developing Clarity smart contracts. See the testnet documentation for more information on the public testnet.

 Mainnet is the fully functional Stacks 2 blockchain, see the Stacks overview for information on running a Stacks node, mining, stacking, and writing Clarity smart contracts.

Getting started

Download and build stacks-blockchain

The first step is to ensure that you have Rust and the support software installed.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

For building on Windows, follow the rustup installer instructions at https://rustup.rs/

From there, you can clone this repository:

git clone --depth=1 https://github.com/blockstack/stacks-blockchain.git

cd stacks-blockchain

Then build the project:

cargo build

Run the tests:

cargo test testnet  -- --test-threads=1

Encode and sign transactions

Here, we have generated a keypair that will be used for signing the upcoming transactions:

cargo run --bin blockstack-cli generate-sk --testnet

# Output
# {
#  secretKey: "b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001",
#  publicKey: "02781d2d3a545afdb7f6013a8241b9e400475397516a0d0f76863c6742210539b5",
#  stacksAddress: "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH"
# }

This keypair is already registered in the testnet-follower-conf.toml file, so it can be used as presented here.

We will interact with the following simple contract kv-store. In our examples, we will assume this contract is saved to ./kv-store.clar:

(define-map store { key: (string-ascii 32) } { value: (string-ascii 32) })

(define-public (get-value (key (string-ascii 32)))
    (match (map-get? store { key: key })
        entry (ok (get value entry))
        (err 0)))

(define-public (set-value (key (string-ascii 32)) (value (string-ascii 32)))
    (begin
        (map-set store { key: key } { value: value })
        (ok true)))

We want to publish this contract on chain, then issue some transactions that interact with it by setting some keys and getting some values, so we can observe read and writes.

Our first step is to generate and sign, using your private key, the transaction that will publish the contract kv-store. To do that, we will use the subcommand:

cargo run --bin blockstack-cli publish --help

With the following arguments:

cargo run --bin blockstack-cli publish b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001 515 0 kv-store ./kv-store.clar --testnet

The 515 is the transaction fee, denominated in microSTX. Right now, the testnet requires one microSTX per byte minimum, and this transaction should be less than 515 bytes. The third argument 0 is a nonce, that must be increased monotonically with each new transaction.

This command will output the binary format of the transaction. In our case, we want to pipe this output and dump it to a file that will be used later in this tutorial.

cargo run --bin blockstack-cli publish b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001 515 0 kv-store ./kv-store.clar --testnet | xxd -r -p > tx1.bin

Run the testnet

You can observe the state machine in action locally by running:

cargo stacks-node start --config=./testnet/stacks-node/conf/testnet-follower-conf.toml

testnet-follower-conf.toml is a configuration file that you can use for setting genesis balances or configuring Event observers. You can grant an address an initial account balance by adding the following entries:

[[ustx_balance]]
address = "ST2VHM28V9E5QCRD6C73215KAPSBKQGPWTEE5CMQT"
amount = 100000000

The address field is the Stacks testnet address, and the amount field is the number of microSTX to grant to it in the genesis block. The addresses of the private keys used in the tutorial below are already added.

Publish your contract

Assuming that the testnet is running, we can publish our kv-store contract.

In another terminal (or file explorer), you can move the tx1.bin generated earlier, to the mempool:

curl -X POST -H "Content-Type: application/octet-stream" --data-binary @./tx1.bin http://localhost:20443/v2/transactions

In the terminal window running the testnet, you can observe the state machine's reactions.

Reading from / Writing to the contract

Now that our contract has been published on chain, let's try to submit some read / write transactions. We will start by trying to read the value associated with the key foo.

To do that, we will use the subcommand:

cargo run --bin blockstack-cli contract-call --help

With the following arguments:

cargo run --bin blockstack-cli contract-call b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001 500 1 ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH kv-store get-value -e \"foo\" --testnet | xxd -r -p > tx2.bin

contract-call generates and signs a contract-call transaction.

We can submit the transaction by moving it to the mempool path:

curl -X POST -H "Content-Type: application/octet-stream" --data-binary @./tx2.bin http://localhost:20443/v2/transactions

Similarly, we can generate a transaction that would be setting the key foo to the value bar:

cargo run --bin blockstack-cli contract-call b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001 500 2 ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH kv-store set-value -e \"foo\" -e \"bar\" --testnet | xxd -r -p > tx3.bin

And submit it by moving it to the mempool path:

curl -X POST -H "Content-Type: application/octet-stream" --data-binary @./tx3.bin http://localhost:20443/v2/transactions

Finally, we can issue a third transaction, reading the key foo again, for ensuring that the previous transaction has successfully updated the state machine:

cargo run --bin blockstack-cli contract-call b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001 500 3 ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH kv-store get-value -e \"foo\" --testnet | xxd -r -p > tx4.bin

And submit this last transaction by moving it to the mempool path:

curl -X POST -H "Content-Type: application/octet-stream" --data-binary @./tx4.bin http://localhost:20443/v2/transactions

Congratulations, you can now write your own smart contracts with Clarity.

Platform support

Officially supported platforms: Linux 64-bit, MacOS 64-bit, Windows 64-bit.

Platforms with second-tier status (builds are provided but not tested): MacOS Apple Silicon (ARM64), Linux ARMv7, Linux ARM64.

For help cross-compiling on memory-constrained devices, please see the community supported documentation here: Cross Compiling.

Community

Beyond this Github project, Blockstack maintains a public forum and an opened Discord channel. In addition, the project maintains a mailing list which sends out community announcements.

The greater Blockstack community regularly hosts in-person meetups. The project's YouTube channel includes videos from some of these meetups, as well as video tutorials to help new users get started and help developers wrap their heads around the system's design.

Further Reading

You can learn more by visiting the Blockstack Website and checking out the documentation:

You can also read the technical papers:

If you have high-level questions about Blockstack, try searching our forum and start a new question if your question is not answered there.

Contributing

Tests and Coverage

PRs must include test coverage. However, if your PR includes large tests or tests which cannot run in parallel (which is the default operation of the cargo test command), these tests should be decorated with #[ignore]. If you add #[ignore] tests, you should add your branch to the filters for the all_tests job in our circle.yml (or if you are working on net code or marf code, your branch should be named such that it matches the existing filters there).

A test should be marked #[ignore] if:

  1. It does not always pass cargo test in a vanilla environment (i.e., it does not need to run with --test-threads 1).
  2. Or, it runs for over a minute via a normal cargo test execution (the cargo test command will warn if this is not the case).

Formatting

This repository uses the default rustfmt formatting style. PRs will be checked against rustfmt and will fail if not properly formatted.

You can check the formatting locally via:

cargo fmt --all -- --check

You can automatically reformat your commit via:

cargo fmt --all

Mining

Stacks tokens (STX) are mined by transferring BTC via PoX. To run as a miner, you should make sure to add the following config fields to your config file:

[node]
# Run as a miner
miner = True
# Bitcoin private key to spend
seed = "YOUR PRIVATE KEY"
# How long to wait for microblocks to arrive before mining a block to confirm them (in milliseconds)
wait_time_for_microblocks = 10000
# Run as a mock-miner, to test mining without spending BTC. Needs miner=True.
#mock_mining = True

[miner]
# Smallest allowed tx fee, in microSTX
min_tx_fee = 100
# Time to spend on the first attempt to make a block, in milliseconds.
# This can be small, so your node gets a block-commit into the Bitcoin mempool early.
first_attempt_time_ms = 1000
# Time to spend on subsequent attempts to make a block, in milliseconds.
# This can be bigger -- new block-commits will be RBF'ed.
subsequent_attempt_time_ms = 60000
# Time to spend mining a microblock, in milliseconds.
microblock_attempt_time_ms = 30000

You can verify that your node is operating as a miner by checking its log output to verify that it was able to find its Bitcoin UTXOs:

$ head -n 100 /path/to/your/node/logs | grep -i utxo
INFO [1630127492.031042] [testnet/stacks-node/src/run_loop/neon.rs:146] [main] Miner node: checking UTXOs at address: <redacted>
INFO [1630127492.062652] [testnet/stacks-node/src/run_loop/neon.rs:164] [main] UTXOs found - will run as a Miner node

Configuring Cost and Fee Estimation

Fee and cost estimators can be configured via the config section [fee_estimation]:

[fee_estimation]
cost_estimator = naive_pessimistic
fee_estimator = fuzzed_weighted_median_fee_rate
fee_rate_fuzzer_fraction = 0.1
fee_rate_window_size = 5
cost_metric = proportion_dot_product
log_error = true
enabled = true

Fee and cost estimators observe transactions on the network and use the observed costs of those transactions to build estimates for viable fee rates and expected execution costs for transactions. Estimators and metrics can be selected using the configuration fields above, though the default values are the only options currently. log_error controls whether or not the INFO logger will display information about the cost estimator accuracy as new costs are observed. Setting enabled = false turns off the cost estimators. Cost estimators are not consensus-critical components, but rather can be used by miners to rank transactions in the mempool or client to determine appropriate fee rates for transactions before broadcasting them.

The fuzzed_weighted_median_fee_rate uses a median estimate from a window of the fees paid in the last fee_rate_window_size blocks. Estimates are then randomly "fuzzed" using uniform random fuzz of size up to fee_rate_fuzzer_fraction of the base estimate.

Non-Consensus Breaking Release Process

For non-consensus breaking releases, this project uses the following release process:

The release must be timed so that it does not interfere with a prepare phase. The timing of the next Stacking cycle can be found here. A release to mainnet should happen at least 24 hours before the start of a new cycle, to avoid interfering with the prepare phase. So, start by being aware of when the release can happen.

Before creating the release, the release manager must determine the version number for this release. The factors that determine the version number are discussed in Versioning. We assume, in this section, that the change is not consensus-breaking. So, the release manager must first determine whether there are any "non-consensus-breaking changes that require a fresh chainstate". This means, in other words, that the database schema has changed, but an automatic migration was not implemented. Then, the release manager should determine whether this is a feature release, as opposed to a hotfix or a patch. Given the answers to these questions, the version number can be computed.

The release manager enumerates the PRs or issues that would block the release. A label should be applied to each such issue/PR as 2.0.x.y.z-blocker. The release manager should ping these issue/PR owners for updates on whether or not those issues/PRs have any blockers or are waiting on feedback.

The release manager should open a develop -> master PR. This can be done before all the blocker PRs have merged, as it is helpful for the manager and others to see the staged changes.

The release manager must update the CHANGELOG.md file with summaries what was Added, Changed, and Fixed. The pull requests merged into develop can be found here. Note, however, that GitHub apparently does not allow sorting by

merge time, so, when sorting by some proxy criterion, some care should be used to understand which PR's were merged after the last develop -> master release PR. This CHANGELOG.md should also be used as the description of the develop -> master so that it acts as release notes when the branch is tagged.

Once the blocker PRs have merged, the release manager will create a new tag by manually triggering the stacks-blockchain Github Actions workflow against the develop branch, inputting the release candidate tag, 2.0.x.y.z-rc0, in the Action's input textbox.

Once the release candidate has been built, and docker images, etc. are available, the release manager will notify various ecosystem participants to test the release candidate on various staging infrastructure:

  1. Stacks Foundation staging environments.
  2. Hiro PBC testnet network.
  3. Hiro PBC mainnet mock miner.

The release manager will test that the release candidate successfully syncs with the current chain from genesis both in testnet and mainnet. This requires starting the release candidate with an empty chainstate and confirming that it synchronizes with the current chain tip.

If bugs or issues emerge from the rollout on staging infrastructure, the release will be delayed until those regressions are resolved. As regressions are resolved, additional release candidates should be tagged. The release manager is responsible for updating the develop -> master PR with information about the discovered issues, even if other community members and developers may be addressing the discovered issues.

Once the final release candidate has rolled out successfully without issue on the above staging infrastructure, the release manager tags 2 additional stacks-blockchain team members to review the develop -> master PR. If there is a merge conflict in this PR, this is the protocol: open a branch off of develop, merge master into that branch, and then open a PR from this side branch to develop. The merge conflicts will be resolved.

Once reviewed and approved, the release manager merges the PR, and tags the release via the stacks-blockchain Github action by clicking "Run workflow" and providing the release version as the tag (e.g., 2.0.11.1.0) This creates a release and release images. Once the release has been created, the release manager should update the Github release text with the CHANGELOG.md "top-matter" for the release.

Download Details:

Author: Stacks-network
Source Code: https://github.com/stacks-network/stacks-blockchain 
License: GPL-3.0 license

#serverless #blockchain #dns #rust #blockchain 

Stacks-blockchain: The Stacks Blockchain Implementation
Rupert  Beatty

Rupert Beatty

1666132800

OHHTTPStubs: Stub Your Network Requests Easily!

OHHTTPStubs

OHHTTPStubs is a library designed to stub your network requests very easily. It can help you:

  • test your apps with fake network data (stubbed from file) and simulate slow networks, to check your application behavior in bad network conditions
  • write unit tests that use fake network data from your fixtures.

It works with NSURLConnection, NSURLSession, AFNetworking, Alamofire or any networking framework that use Cocoa's URL Loading System.


Documentation & Usage Examples

OHHTTPStubs headers are fully documented using Appledoc-like / Headerdoc-like comments in the header files. You can also read the online documentation here.

Basic example

In Objective-C

[HTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
  return [request.URL.host isEqualToString:@"mywebservice.com"];
} withStubResponse:^HTTPStubsResponse*(NSURLRequest *request) {
  // Stub it with our "wsresponse.json" stub file (which is in same bundle as self)
  NSString* fixture = OHPathForFile(@"wsresponse.json", self.class);
  return [HTTPStubsResponse responseWithFileAtPath:fixture
            statusCode:200 headers:@{@"Content-Type":@"application/json"}];
}];

In Swift

This example is using the Swift helpers found in OHHTTPStubsSwift.swift provided by the OHHTTPStubs/Swift subspec or OHHTTPStubs package.

stub(condition: isHost("mywebservice.com")) { _ in
  // Stub it with our "wsresponse.json" stub file (which is in same bundle as self)
  let stubPath = OHPathForFile("wsresponse.json", type(of: self))
  return fixture(filePath: stubPath!, headers: ["Content-Type":"application/json"])
}

Note: if you're using OHHTTPStubs's Swiftier API (OHHTTPStubsSwift.swift and the Swift subspec or OHTTPStubsSwift package), you can also compose the matcher functions like this: stub(isScheme("http") && isHost("myhost")) { … }

More examples & Help Topics

Recording requests to replay them later

Instead of writing the content of the stubs you want to use manually, you can use tools like SWHttpTrafficRecorder to record network requests into files. This way you can later use those files as stub responses.
This tool can record all three formats that are supported by OHHTTPStubs (the HTTPMessage format, the simple response boby/content file, and the Mocktail format).

(There are also other ways to perform a similar task, including using curl -is <url> >foo.response to generate files compatible with the HTTPMessage format, or using other network recording libraries similar to SWHttpTrafficRecorder).

Compatibility

  • OHHTTPStubs is compatible with iOS5+, OS X 10.7+, tvOS.
  • OHHTTPStubs also works with NSURLSession as well as any network library wrapping them.
  • OHHTTPStubs is fully compatible with Swift 3.x, 4.x and Swift 5.x.

Nullability annotations have also been added to the ObjC API to allow a cleaner API when used from Swift even if you don't use the dedicated Swift API wrapper provided by OHHTTPStubsSwift.swift.

Updating to Version 9.0+

  • All classes dropped the OH prefix (OHHHTTPStubs -> HTTPStubs, OHHTTPStubsResponse -> HTTPStubsResponse, etc).
  • The OHPathHelpers class was renamed HTTPStubsPathHelpers.
  • No method and module names were changed.

Installing in your projects

CocoaPods

Using CocoaPods is the recommended way.

  • If you intend to use OHHTTPStubs from Objective-C only, add pod 'OHHTTPStubs' to your Podfile.
  • If you intend to use OHHTTPStubs from Swift, add pod 'OHHTTPStubs/Swift' to your Podfile instead.
pod 'OHHTTPStubs/Swift' # includes the Default subspec, with support for NSURLSession & JSON, and the Swiftier API wrappers

All available subspecs

OHHTTPStubs is split into subspecs so that when using Cocoapods, you can get only what you need, no more, no less.

  • The default subspec includes NSURLSession, JSON, and OHPathHelpers
  • The Swift subspec adds the Swiftier API to that default subspec
  • HTTPMessage and Mocktail are opt-in subspecs: list them explicitly if you need them
  • OHPathHelpers doesn't depend on Core and can be used independently of OHHTTPStubs altogether

List of all the subspecs & their dependencies

Here's a list of which subspecs are included for each of the different lines you could use in your Podfile:

SubspecCoreNSURLSessionJSONSwiftOHPathHelpersHTTPMessageMocktail
pod 'OHHTTPStubs'   
pod 'OHHTTPStubs/Default'   
pod 'OHHTTPStubs/Swift'  
pod 'OHHTTPStubs/Core'      
pod 'OHHTTPStubs/NSURLSession'     
pod 'OHHTTPStubs/JSON'     
pod 'OHHTTPStubs/OHPathHelpers'      
pod 'OHHTTPStubs/HTTPMessage'     
pod 'OHHTTPStubs/Mocktail'     

Swift Package Manager

OHHTTPStubs is compatible with Swift Package Manager, and provides 2 targets for consumption: OHHTTPStubs and OHHTTPStubsSwift.

  • OHHTTPStubs is equivalent to the OHHTTPStubs subspec.
  • OHHTTPStubsSwift is equivalent to the OHHTTPStubs/Swift subspec.

Note: We currently do not have support for the HTTPMessage or Mocktail subspecs in Swift Package Manager. If you are interested in these, please open an issue to explain your needs.

Carthage

OHHTTPStubs is also compatible with Carthage. Just add it to your Cartfile.

Note: The OHHTTPStubs.framework built with Carthage will include all features of OHHTTPStubs turned on (in other words, all subspecs of the pod), including NSURLSession and JSON support, OHPathHelpers, HTTPMessage and Mocktail support, and the Swiftier API.

Using the right Swift version for your project

OHHTTPStubs supports Swift 3.0 (Xcode 8+), Swift 3.1 (Xcode 8.3+), Swift 3.2 (Xcode 9.0+), Swift 4.0 (Xcode 9.0+), Swift 4.1 (Xcode 9.3+), Swift 4.2 (Xcode 10+), Swift 5.0 (Xcode 10.2), and Swift 5.1 (Xcode 11) however we are only testing Swift 4.x (using Xcode 9.1 and 10.1) and Swift 5.x (using Xcode 10.2 AND 11) in CI.

Here are some details about the correct setup you need depending on how you integrated OHHTTPStubs into your project.

CocoaPods: nothing to do

If you use CocoaPods version 1.1.0.beta.1 or later, then CocoaPods will compile OHHTTPStubs with the right Swift Version matching the one you use for your project automatically. You have nothing to do! 🎉

For more info, see CocoaPods/CocoaPods#5540 and CocoaPods/CocoaPods#5760.

Carthage: choose the right version

The project is set up with SWIFT_VERSION=5.0 on master.

This means that the framework on master will build using:

  • Swift 5.1 on Xcode 11
  • Swift 5.0 on Xcode 10.2
  • Swift 4.2 on Xcode 10.1
  • Swift 4.0 on Xcode 9.1

If you want Carthage to build the framework with Swift 3.x you can:

  • either use an older Xcode version
  • or use the previous version of OHHTTPStubs (6.2.0) — whose master branch uses 3.0
  • or fork the repo just to change the SWIFT_VERSION build setting to 3.0
  • or build the framework passing a SWIFT_VERSION to carthage via XCODE_XCCONFIG_FILE=<config file declaring SWIFT_VERSION> carthage build

Special Considerations

Using OHHTTPStubs in your unit tests

OHHTTPStubs is ideal to write unit tests that normally would perform network requests. But if you use it in your unit tests, don't forget to:

  • remove any stubs you installed after each test — to avoid those stubs to still be installed when executing the next Test Case — by calling [HTTPStubs removeAllStubs] in your tearDown method. see this wiki page for more info
  • be sure to wait until the request has received its response before doing your assertions and letting the test case finish (like for any asynchronous test). see this wiki page for more info

Automatic loading

OHHTTPStubs is automatically loaded and installed (at the time the library is loaded in memory), both for:

  • requests made using NSURLConnection or [NSURLSession sharedSession]thanks to this code
  • requests made using a NSURLSession that was created via [NSURLSession sessionWithConfiguration:…] and using either [NSURLSessionConfiguration defaultSessionConfiguration] or [NSURLSessionConfiguration ephemeralSessionConfiguration] configuration — thanks to method swizzling done here in the code.

If you need to disable (and re-enable) OHHTTPStubs — globally or per NSURLSession — you can use [HTTPStubs setEnabled:] / [HTTPStubs setEnabled:forSessionConfiguration:].

Known limitations

  • OHHTTPStubs can't work on background sessions (sessions created using [NSURLSessionConfiguration backgroundSessionConfiguration]) because background sessions don't allow the use of custom NSURLProtocols and are handled by the iOS Operating System itself.
  • OHHTTPStubs don't simulate data upload. The NSURLProtocolClient @protocol does not provide a way to signal the delegate that data has been sent (only that some has been loaded), so any data in the HTTPBody or HTTPBodyStream of an NSURLRequest, or data provided to -[NSURLSession uploadTaskWithRequest:fromData:]; will be ignored, and more importantly, the -URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: delegate method will never be called when you stub the request using OHHTTPStubs.
  • OHTTPStubs has a known issue with redirects that we believe is an Apple bug. It has been discussed here and here. The actual result of this bug is that redirects with a zero second delay may nondeterministically end up with a null response.

As far as I know, there's nothing we can do about those three limitations. Please let me know if you know a solution that would make that possible anyway.

Submitting to the App Store

OHHTTPStubs can be used on apps submitted on the App Store. It does not use any private API and nothing prevents you from shipping it.

But you generally only use stubs during the development phase and want to remove your stubs when submitting to the App Store. So be careful to only include OHHTTPStubs when needed (only in your test targets, or only inside #if DEBUG sections, or by using per-Build-Configuration pods) to avoid forgetting to remove it when the time comes that you release for the App Store and you want your requests to hit the real network!

License and Credits

This project and library has been created by Olivier Halligon (@aligatr on Twitter) and is under the MIT License.

It has been inspired by this article from InfiniteLoop.dk.

I would also like to thank:

  • Sébastien Duperron (@Liquidsoul) for helping me maintaining this library, triaging and responding to issues and PRs
  • Kevin Harwood (@kcharwood) for migrating the code to NSInputStream
  • Jinlian Wang (@JinlianWang) for adding Mocktail support
  • and everyone else who contributed to this project on GitHub somehow.

If you want to support the development of this library, feel free to Donate. Thanks to all contributors so far!

Download Details:

Author: AliSoftware
Source Code: https://github.com/AliSoftware/OHHTTPStubs 
License: MIT license

#swift #mock #ios #objective-c #network 

OHHTTPStubs: Stub Your Network Requests Easily!
Elian  Harber

Elian Harber

1665065160

Redismock: Redis Client Mock

Redis client Mock

Provide mock test for redis query, Compatible with github.com/go-redis/redis/v8

Install

Confirm that you are using redis.Client the version is github.com/go-redis/redis/v8

go get github.com/go-redis/redismock/v8

Quick Start

RedisClient

var ctx = context.TODO()

func NewsInfoForCache(redisDB *redis.Client, newsID int) (info string, err error) {
    cacheKey := fmt.Sprintf("news_redis_cache_%d", newsID)
    info, err = redisDB.Get(ctx, cacheKey).Result()
    if err == redis.Nil {
        // info, err = call api()
        info = "test"
        err = redisDB.Set(ctx, cacheKey, info, 30 * time.Minute).Err()
    }
    return
}

func TestNewsInfoForCache(t *testing.T) {
    db, mock := redismock.NewClientMock()

    newsID := 123456789
    key := fmt.Sprintf("news_redis_cache_%d", newsID)

    // mock ignoring `call api()`

    mock.ExpectGet(key).RedisNil()
    mock.Regexp().ExpectSet(key, `[a-z]+`, 30 * time.Minute).SetErr(errors.New("FAIL"))

    _, err := NewsInfoForCache(db, newsID)
    if err == nil || err.Error() != "FAIL" {
        t.Error("wrong error")
    }

    if err := mock.ExpectationsWereMet(); err != nil {
        t.Error(err)
    }
}

RedisCluster

clusterClient, clusterMock := redismock.NewClusterMock()

Unsupported Command

RedisClient:

  • Subscribe / PSubscribe

RedisCluster

  • Subscribe / PSubscribe
  • Pipeline / TxPipeline
  • Watch
  • DBSize

Download Details:

Author: Go-redis
Source Code: https://github.com/go-redis/redismock 
License: BSD-2-Clause license

#go #golang #redis #mock 

Redismock: Redis Client Mock