我在Xcode 6(Beta 5)中使用XCTestExpectations进行异步测试。我每次运行时都会单独传递所有异步测试。但是,当我尝试运行我的整个套件时,一些测试没有通过,应用程序崩溃了。
我得到的错误是API violation - multiple calls made to -[XCTestExpectation fulfill]
。实际上,在单一方法中并非如此;我的测试的一般格式如下所示:
- (void) someTest {
/* Declare Expectation */
XCTestExpectation *expectation = [self expectationWithDescription:@"My Expectation"];
[MyClass loginOnServerWithEmail:@"example@email.com" andPassword:@"asdfasdf" onSuccess:^void(User *user) {
/* Make some assertions here about the object that was given. */
/* Fulfill the expectation */
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
/* Error handling here */
}];
}
同样,这些测试确实在单独运行时通过,并且它们实际上正在发出网络请求(完全按预期工作),但是一起,测试集合无法运行。
我能够看一下这篇文章here,但无法让解决方案为我工作。
此外,我正在运行OSX Mavericks并使用Xcode 6(Beta 5)。
答案 0 :(得分:13)
我不认为使用__weak
或__block
是一种好方法。我已经使用XCTestExpectation
写了很多单元测试一段时间,直到现在才遇到这个问题。我终于发现问题的真正原因可能导致我的应用程序中的错误。我的问题的根本原因是startAsynchronousTaskWithDuration
多次调用completionHandler。修复后,API违规消失了!
[self startAsynchronousTaskWithDuration:4 completionHandler:^(id result, NSError *error) {
XCTAssertNotNil(result);
XCTAssertNil(error);
[expectation fulfill];
}];
虽然我花了几个小时来修复我的单元测试,但我开始意识到API违规错误,这将有助于我避免在我的应用程序中出现运行时问题。
答案 1 :(得分:4)
设置WITH child
AS (SELECT taskid,
MIN(JobLogDateTime) AS Endtime
FROM JobLog
WHERE taskid = 456
GROUP BY taskid)
SELECT MAX(j.JobLogDateTime) AS Startime,
MIN(c.Endtime) AS Endtime
FROM JobLog AS j
JOIN child AS c ON j.TaskID = c.TaskID;
时出现相同的错误。
exception.expectedFulfillmentCount
如果exception.assertForOverFulfill = false
和exception.assertForOverFulfill = true
在已实现时被调用,则会引发异常
答案 2 :(得分:1)
由于未捕获的异常而终止应用程序 “ NSInternalInconsistencyException”,原因:“违反API-多个 对-[XCTestExpectation履行]的调用无论如何。”
我收到以下错误,并显示以下代码:
func testMultipleWaits() {
let exp = expectation(description: "whatever")
for _ in 0...10 {
DispatchQueue.main.async {
exp.fulfill()
}
wait(for: [exp], timeout: 1)
}
}
意味着以下更改仍无法解决,因为那样您仍需要等待多次。这会给您以下错误。
失败:捕获到“ NSInternalInconsistencyException”,“ API违反- 期望只能等待一次,
whatever
已经 等待”
func testMultipleWaits() {
let exp = expectation(description: "whatever")
for i in 0...10 {
DispatchQueue.main.async {
if i == 6 {
exp.fulfill()
}
}
wait(for: [exp], timeout: 1)
}
}
令人困惑的是,尽管上述更改仍然崩溃,但测试的确完成了,并给您带来了“异步等待失败:超过1秒的超时,并带有未实现的期望:“无论如何”。”
这是一种误导,因为您可能会花时间修复测试,而同时您应该花时间修复崩溃,这是根本原因
此处的修复程序是在for循环的内部中设置期望值。这样,期望就可以实现,并且每次期望都只能等待一次。
func testMultipleWaits() {
for _ in 0...10 {
let exp = expectation(description: "whatever")
DispatchQueue.main.async {
exp.fulfill()
}
wait(for: [exp], timeout: 1)
}
}
答案 3 :(得分:0)
我认为你可能会在某个地方出现一个保留周期问题,导致无法释放你的对象,而这个对象正在调用多次实现预期的块。
否则,如果这是预期的行为,你的期望被多次调用,我写了一个小扩展,允许指定期望数:
import XCTest
extension XCTestExpectation {
private class IntWrapper {
let value :Int!
init?(value:Int?) {
self.value = value
if (value == nil) {
return nil
}
}
}
private struct AssociatedKey {
static var expectationCountKey = ""
}
var expectationCount:Int? {
get {
return objc_getAssociatedObject(self, &AssociatedKey.expectationCountKey) as? Int
}
set {
objc_setAssociatedObject(self, &AssociatedKey.expectationCountKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
func decrementalFulfill() {
guard let expectationCount = self.expectationCount else {
fulfill()
return
}
self.expectationCount = expectationCount - 1
if self.expectationCount <= 0 {
fulfill()
}
}
}
完整代码(带测试:)在这里:https://gist.github.com/huguesbr/7d110bffd043e4d11f2886693c680b06
答案 4 :(得分:0)
那些坚持API违规错误的人,可能self.expectation()
不是您的朋友。相反,您可以尝试常规的初始化:
let expectation = XCTestExpectation(description: "")
检查this的详细信息。
答案 5 :(得分:0)
我不知道为什么它多次调用已完成,我通过执行以下操作解决了这个问题:
let tokenReceived = expectation(description: "Token Received")
var fulfilled = false
mymanager.fetchToken(task: "token", callbackBlock: {token, error in
DispatchQueue.main.async {
tokenString = token
debugPrint("Token found:\(token)")
if(!fulfilled)
{
fulfilled.toggle()
tokenReceived.fulfill()
}
}
})
答案 6 :(得分:0)
在 Xcode 12.5 / Swift 5 上有同样的问题。
检查您是否没有覆盖 setUp 和 tearDown 'class' 方法。
应该是:
override func setUp()
override func tearDown()
代替:
override class func setUp()
override class func tearDown()
答案 7 :(得分:0)
我的问题也是我的代码的线程问题。我没有删除完成处理程序字典中的值。然后,该值被我的下一个测试再次调用完成处理程序(并再次满足预期),从而导致错误。
请注意:找到/修复这些符合您的最大利益,不要假设预期或 Xcode 有问题。
答案 8 :(得分:-1)
这可能是您正在寻找的答案:
XCTestExpectation: how to avoid calling the fulfill method after the wait context has ended?
它至少为我解决了这个问题。
答案 9 :(得分:-3)
尝试将expectationWithDescription声明为弱并打开可选的“预期”变量。
weak var asyncExpectation = expectationWithDescription("expectation")
check for options in the block.
if let asyncExpectation = asyncExpectation{
asyncExpectation.fulfill()
}
这可以避免重新分配asyncExpectation变量并在nil上调用您的期望。