2017-08-27 71 views
1

我有一个函数不需要每10秒钟调用一次。每次调用该函数时,我都会将计时器重置为10秒。使用XCTest在Xcode中测试定时器

class MyClass { 
    var timer:Timer? 

    func resetTimer() { 
    self.timer?.invalidate() 
    self.timer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) { 
     (timer) -> Void in 
     self.performAction()   
    } 
    } 

    func performAction() { 
    // perform action, then 
    self.resetTimer() 
    } 
} 

我想测试的performAction调用()的定时器手动重置为10秒,但我似乎无法找到什么好办法做到这一点。 stubbing resetTimer()觉得测试不会真正告诉我足够的功能。我错过了什么吗?

XCTest:

func testTimerResets() { 
    let myObject = MyClass() 
    myObject.resetTimer() 
    myObject.performAction() 

    // Test that my timer has been reset. 
} 

谢谢!

+1

如果您希望测试等待某种异步情况,则可以使用'XCTestExpectation'。 – Rob

+0

@Rob在myObject.timer上的代码效果也是同步的,所以不需要XCTestExpectation,对吧? – lonesomewhistle

回答

0

我最终存储了原始的Timer的fireDate,然后检查看执行操作后新的fireDate被设置为比原始fireDate晚。

func testTimerResets() { 
    let myObject = MyClass() 
    myObject.resetTimer() 
    let oldFireDate = myObject.timer!.fireDate 
    myObject.performAction() 

    // If timer did not reset, these will be equal 
    XCTAssertGreaterThan(myObject.timer!.fireDate, oldFireDate) 
} 
1

首先,我会说,我不知道你的对象是如何工作的,当你没有任何成员叫refreshTimer

class MyClass { 
    private var timer:Timer? 
    public var starting:Int = -1 // to keep track of starting time of execution 
    public var ending:Int = -1 // to keep track of ending time 


    init() {} 

    func invoke() { 
     // timer would be executed every 10s 
     timer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(performAction), userInfo: nil, repeats: true) 
     starting = getSeconds() 
     print("time init:: \(starting) second") 

    } 

    @objc func performAction() { 
     print("performing action ... ") 
     /* 
     say that the starting time was 55s, after 10s, we would get 05 seconds, which is correct. However for testing purpose if we get a number from 1 to 9 we'll add 60s. This analogy works because ending depends on starting time 
     */ 
     ending = (1...9).contains(getSeconds()) ? getSeconds() + 60 : getSeconds() 
     print("time end:: \(ending) seconds") 
     resetTimer() 
    } 

    private func resetTimer() { 
     print("timer is been reseted") 
     timer?.invalidate() 
     invoke() 
    } 

    private func getSeconds()-> Int { 
     let seconds = Calendar.current.component(.second, from: Date()) 
     return seconds 
    } 

    public func fullStop() { 
     print("Full Stop here") 
     timer?.invalidate() 
    } 
} 

测试(在评论中说明)

let testObj = MyClass() 
    // at init both starting && ending should be -1 
    XCTAssertEqual(testObj.starting, -1) 
    XCTAssertEqual(testObj.ending, -1) 

    testObj.invoke() 
    // after invoking, the first member to be changed is starting 
    let startTime = testObj.starting 
    XCTAssertNotEqual(startTime, -1) 
    /* 
    - at first run, ending is still -1 
    - let's for wait 10 seconds 
    - you should use async method, XCTWaiter and expectation here 
    - this is just to give you a perspective or way of structuring your solution 
    */ 
    DispatchQueue.main.asyncAfter(deadline: .now() + 10) { 
     let startTimeCopy = startTime 
     let endingTime = testObj.ending 
     XCTAssertNotEqual(endingTime, -1) 
     // take the difference between start and end 
     let diff = endingTime - startTime 
     print("diff \(diff)") 
     // no matter the time, diff should be 10 
     XCTAssertEqual(diff, 10) 

     testObj.fullStop() 
    } 

这是不是最好的做这件事的方式,但它可以让你查看或如何流你应该达到这个:)

+0

很酷 - 我的refreshTimer不好 - 刚刚编辑。 – lonesomewhistle

0

如果你想等待定时器启动,你仍然需要使用期望(或Xcode 9的新异步测试API)。

问题在于你想要测试的是什么。你可能不想测试计时器开火,而是想测试计时器的处理程序实际上在做什么。 (想必你必须以执行一些有意义的计时器,所以这就是我们应该测试。)

WWDC 2017年视频Engineering for Testability提供了一个很好的框架内思考如何设计代码进行单元测试,这需要:

  • 控制输入;
  • 对输出的可见性;和
  • 没有隐藏状态。

那么,您的测试有哪些输入?而且,更重要的是,输出是什么。你想在你的单元测试中测试什么断言?

视频还显示人们可能如何重构代码,通过明智地使用来实现这一结构的几个实际的例子:

  • 协议和参数;和
  • 分离逻辑和效果。

如果不知道计时器实际上在做什么,很难进一步提出建议。也许你可以编辑你的问题并澄清。

相关问题