I'm currently using Swift.
My question is simple, how do you unit test a method that create an object in a database with NSDate() as a property?
The problem is that on my assert I'll create a new NSDate() like this:
let expectedObject = Object(data: NSDate())
XCTAssertEqual(database.objects(), [expectedObject])
And the assert will fail because the 2 NSDates are slightly different.
Thank you.
A good way to test methods that use Date() (or CLLocationManager, ReachabilityManager, or anything else static), is to set up an environment for your entire app which can be mocked in tests.
To do this, create an Environment class like so:
struct Environment {
var date: () -> Date = Date.init
}
var Env = Environment()
Throughout the rest of your app, access the current date via Env.date()
instead of calling Date()
.
In your tests, create a mock environment like this:
extension Environment {
static let mock = Environment(
date: {
return Date(year: 2019, month: 11, day: 12, hour: 2, minute: 45, second: 0, millisecond: 0, timeZone: TimeZone.init(abbreviation: "CST"))
}
)
}
And in the setup for your tests, replace the app environment with the mock env like this:
override func setUp() {
super.setUp()
Env = .mock
}
I'd recommend putting that in a base class that all your test classes will inherit from so every test will use the mock environment.
Now when you run your app you will see the current date, but in tests the date will always come back as Nov. 12, 2019 02:45, so you can verify whatever you want from that known date.
For a working example of this setup, I've created a little demo project here. Environment.swift, MockEnvironment.swift, DateExamplesViewController.swift, and DateTests.swift are the relevant files.
As pointed out in https://testdriven-ios.com
a very good approach is to override the NSDate.date in the test case forcing it to return a known date
@implementation NSDate (custom)
+ (instancetype)date{
NSDateFormatter* formatter = NSDateFormatter.new;
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ssZZZ"];
return [formatter dateFromString:@"2017-12-28 13:00:10+0000"];
}
@end
Then everywhere where NSDate.date is called, the 2017-12-28 13:00:10+0000
will be returned
@implementation NSDate (custom)
+ (instancetype)date{
NSDateFormatter* formatter = NSDateFormatter.new;
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ssZZZ"];
return [formatter dateFromString:theDate];
}
@end
you can even customize it further by using a static date
variable and then modify it in the test
-(void) test_something_out {
theDate = @"2017-12-28 13:00:10+0000";
//Test any function that uses nsdate.date
}
If you can use Date.now()
like this, it will be very easy and clean to implement
extension Date {
static var now: (()->Date) = {
return Date()
}
}
print(Date.now())
Date.now = { Date().addingTimeInterval(43534543) }
print(Date.now())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With