- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我有一个服务类,我想断言两件事
这是我的课
protocol OAuthServiceProtocol {
func initAuthCodeFlow() -> Void
func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
}
class OAuthService: OAuthServiceProtocol {
fileprivate let apiClient: APIClient
init(apiClient: APIClient) {
self.apiClient = apiClient
}
func initAuthCodeFlow() -> Void {
}
func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {
}
}
这是我的测试
class OAuthServiceTests: XCTestCase {
var mockAPIClient: APIClient!
var mockURLSession: MockURLSession!
var sut: OAuthService!
override func setUp() {
mockAPIClient = APIClient()
mockAPIClient.session = MockURLSession(data: nil, urlResponse: nil, error: nil)
sut = OAuthService(apiClient: mockAPIClient)
}
func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")
class OAuthServiceMock: OAuthService {
override func initAuthCodeFlow() -> Void {
}
override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
renderOAuthWebViewExpectation.fulfill()
}
}
}
}
我希望创建一个 OAuthService 的本地子类,将其分配为我的 sut
并调用类似 sut.initAuthCodeFlow()
的东西,然后断言我的期望实现了。
我相信这应该满足第 1 点。但是,当我尝试将其分配为已实现时,我无法访问我的期望,因为出现以下错误
Class declaration cannot close over value 'renderOAuthWebViewExpectation' defined in outer scope
如何将其标记为已完成?
我正在遵循 TDD 方法,所以我知道我的 OAuthService 无论如何都会在此时产生失败的测试*
最佳答案
I was hoping to create a local sub class of
OAuthService
, assign that as my sut and call something like likesut.initAuthCodeFlow()
and then assert that my expectation was fulfilled.
我强烈建议您不要使用这种方法。如果您的 SUT 是子类的一个实例,那么您的测试并不是真正测试 OAuthService
,而是 OAuthService
mock。
此外,如果我们将测试视为一种工具:
那么我会争辩说,测试调用某个函数调用另一个函数并不是一个好的测试。我知道这很苛刻,所以让我来解释一下为什么会这样。
它唯一测试的是 initAuthCodeFlow()
在后台调用 renderOAuthWebView(forService:, queryitems:)
。它对被测系统的实际行为 以及它是否直接生成的输出没有任何断言。如果我要编辑 renderOAuthWebView(forService:, queryitems:)
的实现并添加一些会在运行时崩溃的代码,这个测试就不会失败。
像这样的测试无助于保持代码库易于更改,因为如果您想更改 OAuthService
的实现,可能通过向 renderOAuthWebView(forService: , queryitems:)
或通过将 queryitems
重命名为 queryItems
以匹配大小写,您必须同时更新生产代码和测试代码。换句话说,测试将妨碍您进行重构 - 改变代码的外观而不改变代码的行为方式 - 没有任何额外的好处。
那么,应该如何以一种防止错误并帮助快速移动的方式测试 OAuthService
?诀窍在于测试行为而不是实现。
OAuthService
应该做什么? initAuthCodeFlow()
不返回任何值,因此我们可以检查直接输出,但我们仍然可以检查间接输出、副作用。
我在这里猜测,但我从你的测试中检查了 renderOAuthWebView(forService:, queryitems:)
我会得到一个 APIClient
输入类型我会说它会为某个 URL 呈现某种 WebView ,然后向给定的 APIClient
发出另一个请求,可能使用从 WebView 接收到的 OAuth token ?
测试与 APIClient
交互的一种方法是对要调用的预期端点进行断言。您可以使用 OHHTTPStubs 之类的工具来完成或者使用您的 URLSession
自定义测试替身,记录它收到的请求并允许您检查它们。
对于webview的呈现,可以使用delegate patter,设置一个符合delegate协议(protocol)的test double记录是否被调用。或者您可以在更高级别进行测试并检查运行测试的 UIWindow
以查看 Root View Controller 是否是具有 Web View 的 Controller 。
归根结底,一切都是权衡取舍的问题。您采用的方法并没有错,它只是针对断言代码实现而不是其行为进行了更多优化。我希望通过这个答案,我展示了一种不同类型的优化,一种偏向于行为的优化。根据我的经验,从中长期来看,这种测试方式更有帮助。
关于swift - 如何使用 XCTAssert 验证类方法是否被调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53994172/
如果逻辑失败,如何停止单元测试的执行。下面是例子。如何在 XCTAssertEqual("Hello", "Hi", "Passed") 条件失败时停止执行。 func test_one() {
在 Swift 中,当有人使用 XCTest 断言时,他们可以编写如下断言: XCTAssertTrue(foo.sameAs(bar), "\(foo) is not equal to \(bar)
我正在尝试使用 XCTAssert 编写单元测试。我有一个 NSSet,我想测试这个集合是否包含任何对象。 我检查: XCTAssertTrue((mySet.count == 0), @"mySet
我有一个服务类,我想断言两件事 调用了一个方法 将正确的参数传递给该方法 这是我的课 protocol OAuthServiceProtocol { func initAuthCodeFlow
我经常看到这样的例子,其中 XCTAssert 和 XCTFail 在理论上等待预期的回调调用中使用。如果超时到期,会发生什么? 一个人为的例子(随意考虑 Swift 中的等价物,它们应该是相同的):
为什么会这样 var sb = NSStoryboard(name: "Main", bundle: nil) var wc = sb?.instantiateControllerWithIdenti
我正在学习单元测试和 XCode 的 XCTAssert 语句。 我有一种情况,我想根据环境的设置方式以动态方式按顺序测试一堆东西。 我正在尝试执行以下操作: for (i = 1, etc...)
对于 Swift 的 Dictionary 结构上的泛型方法,我有两个几乎完全相同的断言,但一个成功,另一个失败。我假设这就是 XCTAssert 的工作原理,但不明白为什么。有谁知道为什么? 如果该
XCTAssert() 和 Swift 中的 assert() 有什么区别? 最佳答案 XCTAssert 是来自 XCTest 框架的单元测试断言系列之一,应该只出现在单元测试目标中(即不在您的应用
所以我正在使用 XCTest 进行单元测试,但遇到了一个小问题。 当我编写一个包含速记数组声明和内部方法调用的 XCTAssert 语句时,Xcode 中会出现语法错误: XCTAssert([sel
我有一个类: class Person: NSObject { var name: String init(name: String) { self.name = na
XCTAssert 和 XCTAssertTrue 有什么区别?他们似乎在做同样的事情,如果是这样,为什么我们需要两者? 最佳答案 您建议此处存在冗余是正确的。事实上,它们绝对是相同的——也就是说,在
我是一名优秀的程序员,十分优秀!