MockoloFramework 1.1.2

MockoloFramework 1.1.2

Ellie ShinEllie Shin维护。



CII Best Practices Build Status License FOSSA Status

欢迎使用 Mockolo

Mockolo 是一个高效的 Swift 模拟生成器。Swift 没有提供模拟支持,Mockolo 提供了一种快速简单的方式来自动生成可以在代码中进行测试的模拟对象。Mockolo 的主要目标之一是高性能。与其他框架不同,Mockolo 通过轻量级命令行工具提供高度性能和可扩展的模拟生成,因此它可以作为 linter 或构建过程的一部分运行。尝试 Mockolo 并以有效、性能的方式提高您项目的测试覆盖率。

动机

该项目的主要目标之一是高性能。对于例如包含超过 2M LoC 或超过 10K 协议的大型代码库,没有很多第三方工具能快速运行。它们可能需要几个小时,甚至启用了缓存后也需要几分钟。Mockolo 就是构建用来实现快速生成模拟(以秒的数量级)的(在如此大的代码库上)。它使用了最小必要的框架集合(在“使用的库”部分中提到)以保持代码简洁和高效。

另一个目标是在必要时启用使用或覆写类型的功能。这允许简化一些需要深入分析的特征,例如具有关联类型的协议,使其更加简单、直观和健壮。

免责声明

本项目中可能包含不稳定的 API,可能不适合通用使用。支持和/或新版本可能有限。

系统要求

  • Swift 5.1 或更高版本
  • Xcode 11.2 或更高版本
  • MacOS 10.14.6 或更高版本
  • 支持 Swift 包管理器

构建 / 安装

首先,克隆项目。

$ git clone https://github.com/uber/mockolo.git
$ cd mockolo

可选,查看 Mockolo 的已发布版本列表,通过运行以下命令签出一个。

$ git tag -l
$ git checkout [tag]

选项 1:运行以下命令进行发布构建。

$ swift build -c release

这将在 .build/release 目录中创建一个名为 mockolo 的二进制文件。要安装,只需将此可执行文件复制到包含在您的 PATH 环境变量中的目录。

选项 2:运行 install-script.sh(使用 -h 查看选项)

$ ./install-script.sh -s [source dir] -t mockolo -d [destination dir] -o [output filename]

这将创建一个包含 mockolo 可执行文件以及必要的 SwiftSyntax 解析 dylib(lib_InternalSwiftSyntaxParser.dylib)的 tarball,以便分发。这允许在没有依赖 dylib 所在位置的情况下运行 mockolo。

选项 3:通过 Mint 运行

$ mint run uber/mockolo mockolo -s [source dir] -t mockolo -d [destination dir] -o [output filename]

要使用 Xcode,运行以下命令。

$ swift package generate-xcodeproj

将 MockoloFramework 添加到你的项目

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

运行

Mockolo 是一个命令行可执行文件。要运行它,传入源文件目录或构建目标的文件路径列表,以及模拟输出目标文件路径。要查看命令行的其他参数,请运行 mockolo --help

.build/release/mockolo -s MyDir -d ./MockResults.swift -x Images Strings

这将解析 MyDir 目录下的所有源文件,排除任何以 ImagesStrings 结尾的文件(例如 MyImages.swift),并将模拟生成到 ./MockResults.swift 文件中。

使用 --help 查看完整的参数选项。

.build/release/mockolo --help

OVERVIEW: Mockolo: Swift mock generator.

USAGE: mockolo <options>

OPTIONS:
  --destination, -d       Output file path containing the generated Swift mock classes. If no value is given, the program will exit.
  --sourcedirs, -s        Paths to the directories containing source files to generate mocks for. If the --filelist or --sourcefiles values exist, they will be ignored.
  --sourcefiles, -srcs    List of source files (separated by a comma or a space) to generate mocks for. If the --sourcedirs or --filelist value exists, this will be ignored.
  --filelist, -f          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.
  --exclude-suffixes, -x  List of filename suffix(es) without the file extensions to exclude from parsing (separated by a comma or a space).
  --depfilelist, -deplist Path to a file containing a list of dependent files (separated by a new line) from modules this target depends on. 
  --mockfiles, -mocks     List of mock files (separated by a comma or a space) from modules this target depends on.
  --annotation, -a        A custom annotation string used to indicate if a type should be mocked (default = @mockable).
  --header, -h            A custom header documentation to be added to the beginning of a generated mock file.
  --macro, -m             If set, #if [macro] / #endif will be added to the generated mock file content to guard compilation.
  --concurrency-limit, -j Maximum number of threads to execute concurrently (default = number of cores on the running machine).
  --logging-level, -v     The logging level to use. Default is set to 0 (info only). Set 1 for verbose, 2 for warning, and 3 for error.
  --use-sourcekit         If this argument is added, it will use SourceKit for parsing. By default it uses SwiftSyntax.
  --help                  Displays available options.

如何使用

例如,Foo.swift 包含

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

运行以下命令行 .build/release/mockolo -srcs Foo.swift -d ./MockResults.swift 将生成

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 ""
    }
}

上述模拟现在可以如下用于测试

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

可以传递一个覆盖参数,例如对 typealias,到注解(默认或自定义)中,使用分号分隔。

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

这会生成以下模拟输出

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

待办

目前支持协议模拟。类模拟将在将来添加。

使用的库

SwiftSyntax | SourceKitten

如何为 Mockolo 做贡献

请参阅 CONTRIBUTING 以获取更多信息。

报告任何问题

如果您遇到任何问题,请提交一个 git 问题。请包括

  • 操作系统版本(例如,macOS 10.14.6)
  • 机器上安装的 Swift 版本(从 swift --version
  • Xcode 版本
  • 源代码的特定版本号(您可以使用 git tag 获取所有发布版本列表或 git log 获取特定提交 sha)
  • 机器上的任何本地更改

许可证

Mockolo 使用 Apache License 2.0 许可。更多信息请参阅 LICENSE

Copyright (C) 2017 Uber Technologies

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   https://apache.ac.cn/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.