2015-04-27 25 views
9

Tôi phải kiểm tra một số tính toán ngày nhưng để làm như vậy tôi cần phải thử NSDate() trong Swift. Toàn bộ ứng dụng được viết bằng Swift và tôi cũng muốn viết bài kiểm tra trong đó.Làm thế nào để giả lập NSDate trong Swift?

Tôi đã thử phương pháp nổi loạn nhưng nó không hoạt động (hoặc tôi đang làm điều gì đó sai có nhiều khả năng).

extension NSDate { 
    func dateStub() -> NSDate { 
     println("swizzzzzle") 
     return NSDate(timeIntervalSince1970: 1429886412) // 24/04/2015 14:40:12 
    } 
} 

kiểm tra:

func testCase() { 
    let original = class_getInstanceMethod(NSDate.self.dynamicType, "init") 
    let swizzled = class_getInstanceMethod(NSDate.self.dynamicType, "dateStub") 
    method_exchangeImplementations(original, swizzled) 
    let date = NSDate() 
// ... 
} 

nhưng date luôn là ngày hiện tại.

+0

Tại sao bạn không chỉ cần tắt cập nhật ngày và giờ của hệ thống tự động, thay đổi ngày của máy tính và thực hiện thử nghiệm của bạn –

+2

Chúng tôi chạy thử nghiệm trên mac từ xa được nối với giải pháp CI. Tôi không muốn gây rối với máy tính đó quá nhiều. –

Trả lời

7

Tuyên bố miễn trừ - Tôi mới dùng thử Swift vì vậy đây có thể là một giải pháp khủng khiếp, nhưng tôi cũng đang đấu tranh với điều này, vì vậy hy vọng điều này sẽ giúp đỡ ai đó.

Tôi đã tìm thấy this explanation là trợ giúp lớn.

tôi phải tạo ra một lớp đệm giữa NSDate và mã của tôi:

class DateHandler { 
    func currentDate() -> NSDate! { 
     return NSDate() 
    } 
} 

sau đó sử dụng lớp đệm trong bất kỳ mã mà sử dụng NSDate(), cung cấp mặc định DateHandler() như là một đối số tùy chọn.

class UsesADate { 
    func fiveSecsFromNow(dateHandler: DateHandler = DateHandler()) -> NSDate! { 
     return dateHandler.currentDate().dateByAddingTimeInterval(5) 
    } 
} 

Sau đó, trong các thử nghiệm tạo ra một mô hình được thừa kế từ DateHandler gốc(), và "bơm" đó vào code để được kiểm tra:

class programModelTests: XCTestCase { 

    override func setUp() { 
     super.setUp() 

     class MockDateHandler:DateHandler { 
      var mockedDate:NSDate! = // whatever date you want to mock 

      override func currentDate() -> NSDate! { 
       return mockedDate 
      } 
     } 
    } 

    override func tearDown() { 
     super.tearDown() 
    } 

    func testAddFiveSeconds() { 
     let mockDateHandler = MockDateHandler() 
     let newUsesADate = UsesADate() 
     let resultToTest = usesADate.fiveSecondsFromNow(dateHandler: mockDateHandler) 
     XCTAssertEqual(resultToTest, etc...) 
    } 
} 
+0

Tôi đã kết thúc với giải pháp tương tự vì vậy tôi sẽ chấp nhận câu trả lời này :) –

3

Thay vì sử dụng sự gian lận, bạn nên thiết kế hệ thống của mình để hỗ trợ kiểm tra. Nếu bạn thực hiện nhiều xử lý dữ liệu thì bạn nên chèn ngày thích hợp vào các hàm sử dụng nó. Bằng cách này, thử nghiệm của bạn sẽ đưa ngày tháng vào các hàm này để kiểm tra chúng và bạn có các thử nghiệm khác xác minh rằng ngày chính xác sẽ được tiêm (khi bạn sử dụng các phương thức sử dụng ngày tháng) cho các tình huống khác nhau.

Cụ thể cho vấn đề rắc rối của bạn, IIRC NSDate là cụm lớp sao cho phương pháp bạn đang thay thế không được gọi là lớp khác sẽ được tạo và trả về 'âm thầm'.

3

Nếu bạn muốn sự gian lận đó bạn cần để tạo ra một lớp học được sử dụng trong nội bộ bởi NSDate và nó là __NSPlaceholderDate. Chỉ sử dụng tính năng này để thử nghiệm vì đây là một API riêng tư.

func timeTravel(to date: NSDate, block:() -> Void) { 
    let customDateBlock: @convention(block) (AnyObject) -> NSDate = { _ in date } 
    let implementation = imp_implementationWithBlock(unsafeBitCast(customDateBlock, AnyObject.self)) 
    let method = class_getInstanceMethod(NSClassFromString("__NSPlaceholderDate"), #selector(NSObject.init)) 
    let oldImplementation = method_getImplementation(method) 
    method_setImplementation(method, implementation) 
    block() 
    method_setImplementation(method, oldImplementation) 
} 

Và sau đó bạn có thể sử dụng như thế này:

let date = NSDate(timeIntervalSince1970: 946684800) // 2000-01-01 
timeTravel(to: date) { 
    print(NSDate()) // 2000-01-01 
} 

Như những người khác đề nghị tôi thà khuyên giới thiệu một lớp Clock hoặc tương tự mà bạn có thể vượt qua xung quanh và nhận được một ngày từ nó và bạn có thể dễ dàng thay thế bằng cách thực hiện thay thế trong các thử nghiệm của bạn.

+0

treo trên bộ mô phỏng 32 bit '# 1 \t 0x006ded32 trong (không gian tên ẩn danh) :: AutoreleasePoolPage :: pop (void *)() ' –

+0

It don ' t làm việc với Swift 3 và Date(), tôi không thể tìm ra cách sửa chữa nó –

+1

@ArkadiuszMatecki Không có cách nào để làm cho nó hoạt động với 'Date' được giới thiệu trong Swift vì nó là kiểu Swift gốc và swizzling sẽ không hoạt động với nó. Bạn cần sử dụng phương pháp tiếp cận với một lớp đồng hồ. –

Các vấn đề liên quan