How to mock NSDate in Swift? - unit-testing

I have to test some date calculation but to do so I need to mock NSDate() in Swift. Whole app is written in Swift and I'd like to write test in it as well.
I've tried method swizzling but it doesn't work (or I'm doing something wrong which is more likely).
extension NSDate {
func dateStub() -> NSDate {
println("swizzzzzle")
return NSDate(timeIntervalSince1970: 1429886412) // 24/04/2015 14:40:12
}
}
test:
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()
// ...
}
but date is always current date.

Disclaimer -- I'm new to Swift testing so this may be a horribly hacky solution, but I've been struggling with this, too, so hopefully this will help someone out.
I found this explanation to be a huge help.
I had to create a buffer class between NSDate and my code:
class DateHandler {
func currentDate() -> NSDate! {
return NSDate()
}
}
then used the buffer class in any code that used NSDate(), providing the default DateHandler() as an optional argument.
class UsesADate {
func fiveSecsFromNow(dateHandler: DateHandler = DateHandler()) -> NSDate! {
return dateHandler.currentDate().dateByAddingTimeInterval(5)
}
}
Then in the test create a mock that inherits from the original DateHandler(), and "inject" that into the code to be tested:
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...)
}
}

If you want to swizzle it you need to swizzle a class that is internally used by NSDate and it is __NSPlaceholderDate. Use this only for testing since it is a private API.
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)
}
And later you can use like this:
let date = NSDate(timeIntervalSince1970: 946684800) // 2000-01-01
timeTravel(to: date) {
print(NSDate()) // 2000-01-01
}
As others suggested I would rather recommend introducing a class Clock or similar that you can pass around and get a date from it and you can easily replace it with an alternative implementation in your tests.

Rather than use swizzling you should really design your system to support testing. If you do a lot of data processing then you should inject the appropriate date into the functions which use it. In this way your test injects the dates into these functions to test them and you have other tests which verify that the correct dates will be injected (when you stub the methods that use the dates) for various other situations.
Specifically for your swizzling problem, IIRC NSDate is a class cluster so the method you're replacing is unlikely to be called as a different class will be 'silently' created and returned.

Related

Mockk with context receiver

I'm trying to use Mockk to mock a method with context receiver:
class MyClass {
// The method I'm going to mock
context(CallContext)
fun myMethod(a: Int) Int { a }
}
It's hard to get the instance of CallContext in the unit test. So I hope I can write a unit test in this way:
/*
This should work, but I can't get the CallContext instance
with(callContextInstance) {
Every { mockedMyClass.myMethod(1) } returns 2
}
*/
// I hope a unit test can be written like this... But it won't compile now.
with(any<CallContext>) {
Every { mockedMyClass.myMethod(1) } returns 2
}
So what should I do? Thanks in advance.
At the time of writing, MockK does not support context receivers, and it probably won't until context receivers are released - so some time after Kotlin 1.9, so maybe in 2024).
(Context receivers are explicitly described as not ready for production. A stable release won't be available until after the K2 release, and the K2 beta is targeted for Kotlin 1.9, which has a planned release of December 2023.)
That said, if anyone wants to attempt support, then get stuck in! MockK is an community supported open source project that accepts PRs.
Confounding factors
However, there are two hinderances before MockK can fully support context receivers:
Context receivers aren't finished, nor is their current implementation stable. KT-10468. Their implementation could change significantly. Trying to implement support for a moving target is challenging.
IDE support is limited, which makes developing with them difficult (follow KTIJ-20857 for updates)
Workaround
In the meantime, you could adjust your code to allow for manual mocking.
First, adjust MyClass to either be an open class, or introduce a new interface that describes the behaviour you want to mock (code to an interface).
/** Describe the API that [MyClass] will implement */
interface MyClassSpec {
context(CallContext)
fun myMethod(a: Int): Int
}
And then implement the interface
/** Concrete implementation of [MyClassSpec] */
class MyClass: MyClassSpec {
context(CallContext)
override fun myMethod(a: Int): Int = a
}
Now in your test you can create a mock by creating an anonymous object that implements MyClassSpec - and now you have a mock that supports context receivers.
#Test
fun myTest() {
val myClassMock = object : MyClassSpec {
context(CallContext)
override fun myMethod(a: Int): Int = 123
}
}
If I get the idea what exactly do you try to mock, the following works with mockk 1.13.3:
interface CallContext
class MyClass {
context(CallContext)
fun myMethod(a: Int): Int = a
}
class ContextMockTest {
private val myClassMock: MyClass = mockk()
#Test
fun mockContextWorks() {
every {
with(any<CallContext>()) {
myClassMock.myMethod(any())
}
} returns 123
val context = object : CallContext { }
with(context) {
assertEquals(123, myClassMock.myMethod(1))
}
verify {
with(any<CallContext>()) {
myClassMock.myMethod(1)
}
}
}
}
A link to the gist just in case

How to test observer?

I would test presenter like this:
class MostPopularPresenter #Inject constructor(val mostPopularUseCase: MostPopularUseCase)
: Presenter<MostPopularView>() {
fun requestMostPopular(page: Int, update: Boolean) {
if (page <= 6)
mostPopularUseCase.execute(MostPopularObserver(), MostPopularUseCase.Params.createQuery(page, 15, update))
}
inner class MostPopularObserver : DisposableSingleObserver<MostPopularModel>() {
override fun onSuccess(t: MostPopularModel) {
this#MostPopularPresenter.view?.populateRecyclerList(t)
}
override fun onError(e: Throwable) {
this#MostPopularPresenter.view?.showError()
}
}
}
I have problem how to mock observer and force it to throw error or return value on success. I'm using mockito/junit. Can someone point me how to achieve it? Maybe my code is untestable?
An observer is an object that shouldn't be really tested. It has been already tested when it has been developed by a third developer, although there are some people that say, with a part of reason, that you should also test a third party library in order to ensure that it doesn't break your code between versions.
So, if you don't test the observer... how do you test your code? Simply, what you really need to test is the presenter itself. The code running inside the observer is part of the presenter. So instead of mocking the observer mock the useCase:
test useCaseFails() {
val usecase = // mock use case
when(usecase.execute(...))
.thenAnswer(/* receive the observer as first parameter
and make it emit an error */)
val presenter = ...
presenter.requestMostPopular(...)
// assert that presenter.view?.showError has been called
}
Another way of doing this (at least this is the way I usually code) is to make the useCase return an observable and subscribe it in the presenter:
class MostPopularPresenter #Inject constructor(val mostPopularUseCase: MostPopularUseCase)
: Presenter<MostPopularView>() {
private var lateinit observer : Disposable
fun requestMostPopular(page: Int, update: Boolean) {
if (page <= 6)
disposable = mostPopularUseCase.execute(MostPopularUseCase.Params.createQuery(page, 15, update))
.subscribe(t -> view?.populateRecyclerList(t),
e -> view?.showError())
}
}
This way you can easily mock your useCase so it returns a Subject you can control:
test useCaseFails() {
val usecase = // mock use case
val subject = PublishSubject()
when(usecase.execute(...))
.thenReturn(subject)
val presenter = ...
presenter.requestMostPopular(...)
subject.emitError(...) // <- pseudocode
// assert that presenter.view?.showError has been called
}
Usually there are not many cases where it is absolutely not possible to test. As far as I see it, you have a few options:
Put the observer into the constructor with a default value (but this might have some downsides with your dependency injection)
Put the observer into the function with a default value. This would work, but you have to choose if your API should contain this
Use the observer as property. In the test you can override this one.
All this variants would work and are listed here:
// observer in constructor
class MostPopularPresenter #Inject constructor(val mostPopularUseCase: MostPopularUseCase, val observer: DisposableSingleObserver<MostPopularModel> = MostPopularObserver())
: Presenter<MostPopularView>() {
// observer as property
internal var observer: DisposableSingleObserver<MostPopularModel> = MostPopularObserver()
// observer in function
fun requestMostPopular(page: Int, update: Boolean, observer: DisposableSingleObserver<MostPopularModel> = MostPopularObserver()) {
if (page <= 6)
mostPopularUseCase.execute(observer, MostPopularUseCase.Params.createQuery(page, 15, update))
}
}
internal class MostPopularObserver : DisposableSingleObserver<MostPopularModel>() { ... }
It would be even nicer, if you us a DisposableSingleObserverFactory and create the observer when it's needed.
class MostPopularPresenter #Inject constructor(val mostPopularUseCase: MostPopularUseCase, val observerFactory: DisposableSingleObserverFactory<MostPopularModel> = MostPopularObserverFactorty())
: Presenter<MostPopularView>() {
internal var observerFactory: DisposableSingleObserverFactory<MostPopularModel> = MostPopularObserverFactory()
fun requestMostPopular(page: Int, update: Boolean, observerFactory: DisposableSingleObserverFactory<MostPopularModel> = MostPopularObserver()) {
if (page <= 6)
mostPopularUseCase.execute(observerFactory.create(), MostPopularUseCase.Params.createQuery(page, 15, update))
}
}
internal class MostPopularObserver : DisposableSingleObserver<MostPopularModel>() {

How to mock single function in Swift?

My question is simple, how to mock a function (not a method) in Swift.
i.e., a standalone function not inside of a class.
Thank you.
EDIT:
Let's say I have the following function:
func distance(c1: CLLocation, c2: CLLocation) {
...
}
And I want to test my class:
class MyClass {
func selectedLocation(location: CLLocation) {
let text = "\(distance(self.currentLocation, location)) meters"
self.view.showText(text)
}
}
How do I mock the distance function?
To mock the distance function, you would need to do something like this
func distance(c1: CLLocation, c2: CLLocation) -> CLLocationDistance {
// ...
}
class MyClass {
var calculateDistance = distance
func selectedLocation(location: CLLocation) {
let text = "\(calculateDistance(self.currentLocation, location)) meters"
self.view.showText(text)
}
}
And in your test code you would need to do this:
func testCalculateDistanceFromLocation() {
let thing = MyClass()
thing.calculateDistance = { c1, c2 in /* return mock distance here */ }
// assert correct text appeared in the view
}
This way you are providing a new implementation of the distance function when in a testing environment. As far as I know you cannot completely replace the body of a top level function dynamically such that you don't need the internal class property that stores that function value.
This is kind of cumbersome though to do this for all your functions, so I say to only do this when you feel you absolutely need to substitute this extra mocked dependency. If possible, I would encourage you to test your class as a wnole unit, if it has few or no other external dependencies and treat the distance function as an implementation detail.
Do not know if I understand this correct. Swift does support global functions.
[update: This is what I do in the unit test]
public func getNumber()->Int //add public for unit testing
{
return 1
}
class MyClass: NSObject
{
func calculate()
{
let num = getNumber()
println(num)
}
}
///unit test case
import MyModule
extension NSObject
{
public fund getNumber()->Int
{
return 5 //mock implementation
}
}
func testExample() {
let myInstance = MyClass()
myInstance.calculate()
}

Derived Class Method of Generic Class Template not being called

I have a generic class for making and processing JSON API requests. I pass in the TParam and TResult template parameters but when I use a derived type it's implementation is not being called.
Here is some code you can throw in a playground to illustrate:
import Cocoa
// Base class for parameters to POST to service
class APIParams {
func getData() -> Dictionary<String, AnyObject> {
return Dictionary<String, AnyObject>()
}
}
// Base class for parsing a JSON Response
class APIResult {
func parseData(data: AnyObject?) {
}
}
// Derived example for a login service
class DerivedAPIParams: APIParams {
var user = "some#one.com"
var pass = "secret"
// THIS METHOD IS CALLED CORRECTLY
override func getData() -> Dictionary<String, AnyObject> {
return [ "user": user, "pass": pass ]
}
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
var success = false
var token:String? = ""
// THIS METHOD IS NEVER CALLED
override func parseData(data: AnyObject?) {
/*
self.success = data!.valueForKey("success") as Bool
self.token = data!.valueForKey("token") as? String
*/
self.success = true
self.token = "1234"
}
}
class APIOperation<TParams: APIParams, TResult: APIResult> {
var url = "http://localhost:3000"
func request(params: TParams, done: (NSError?, TResult?) -> ()) {
let paramData = params.getData()
// ... snip making a request to website ...
let result = self.parseResult(nil)
done(nil, result)
}
func parseResult(data: AnyObject?) -> TResult {
var result = TResult.self()
// This should call the derived implementation if passed, right?
result.parseData(data)
return result
}
}
let derivedOp = APIOperation<DerivedAPIParams, DerivedAPIResult>()
let params = DerivedAPIParams()
derivedOp.request(params) {(error, result) in
if result? {
result!.success
}
}
The really weird thing is that only the DerivedAPIResult.parseData() is not called, whereas the DerivedAPIParams.getData() method is called. Any ideas why?
UPDATE: This defect is fixed with XCode 6.3 beta1 (Apple Swift version 1.2 (swiftlang-602.0.37.3 clang-602.0.37))
Added info for a workaround when using XCode 6.1 (Swift 1.1)
See these dev forum threads for details:
https://devforums.apple.com/thread/251920?tstart=30
https://devforums.apple.com/message/1058033#1058033
In a very similar code sample I was having the exact same issue. After waiting through beta after beta for a "fix", I did more digging and discovered that I can get the expect results by making the base class init() required.
By way of example, here is Matt Gibson's reduced example "fixed" by adding the proper init() to ApiResult
// Base class for parsing a JSON Response
class APIResult {
// adding required init() to base class yields the expected behavior
required init() {}
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
}
class APIOperation<TResult: APIResult> {
init() {
// EDIT: workaround for xcode 6.1, tricking the compiler to do what we want here
let tResultClass : TResult.Type = TResult.self
var test = tResultClass()
// should be able to just do, but it is broken and acknowledged as such by Apple
// var test = TResult()
println(test.self) // now shows that we get DerivedAPIResult
}
}
// Templated creation creates APIResult
let derivedOp = APIOperation<DerivedAPIResult>()
I do not know why this works. If I get time I will dig deeper, but my best guess is that for some reason having required init is causing different object allocation/construction code to be generated that forces proper set up of the vtable we are hoping for.
Looks possibly surprising, certainly. I've reduced your case to something rather simpler, which might help to figure out what's going on:
// Base class for parsing a JSON Response
class APIResult {
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
}
class APIOperation<TResult: APIResult> {
init() {
var test = TResult()
println(test.self) // Shows that we get APIResult, not DerivedAPIResult
}
}
// Templated creation creates APIResult
let derivedOp = APIOperation<DerivedAPIResult>()
...so it seems that creating a new instance of a templated class with a type constraint gives you an instance of the constraint class, rather than the derived class you use to instantiate the specific template instance.
Now, I'd say that the generics in Swift, looking through the Swift book, would probably prefer you not to create your own instances of derived template constraint classes within the template code, but instead just define places to hold instances that are then passed in. By which I mean that this works:
// Base class for parsing a JSON Response
class APIResult {
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
}
class APIOperation<T: APIResult> {
var instance: T
init(instance: T) {
self.instance = instance
println(instance.self) // As you'd expect, this is a DerivedAPIResult
}
}
let derivedOpWithPassedInstance = APIOperation<DerivedAPIResult>(instance: DerivedAPIResult())
...but I'm not clear whether what you're trying should technically be allowed or not.
My guess is that the way generics are implemented means that there's not enough type information when creating the template to create objects of the derived type from "nothing" within the template—so you'd have to create them in your code, which knows about the derived type it wants to use, and pass them in, to be held by templated constrained types.
parseData needs to be defined as a class func which creates an instance of itself, assigns whatever instance properties, and then returns that instance. Basically, it needs to be a factory method. Calling .self() on the type is just accessing the type as a value, not an instance. I'm surprised you don't get some kind of error calling an instance method on a type.

Create a Partial Stub in Microsoft Moles

I am pulling my hair out with this one. I have looked and cannot find a simple, clear example of creating and using a partial stub with Microsoft Moles. Maybe I'm missing somethimg, or have my code architected poorly, but I can't seem to get this to work.
Here's my class (simplified):
public class AccountService : IAccountService {
private readonly webServiceProxy IExternalWebServiceProxy;
public AccountService(IExternalWebServiceProxy webServiceProxy) {
this.webServiceProxy = webServiceProxy;
}
public List<AccountModel> GetAccounts(string customerId) {
var returnList = new List<AccountModel>();
var xmlResponse = webServiceProxy.GetAllCustomerAccounts(customerId);
var accountNodes = xmlResponse.SelectNodes("//AccountNodes");
if (accountNodes != null)
{
foreach (XmlNode node in accountNodes)
{
var account = this.MapAccountFromXml(node);
if (!string.IsNullOrEmpty(account.AccountNumber))
{
returnList.Add(account);
}
}
}
return returnList;
}
public AccountModel MapAccountFromXml(XmlNode node) {
if (!IsValidAccount(node) {
return null;
}
// This performs a lot of XML manipulation getting nodes based on attributes
// and mapping them to the various properties of the AccountModel. It's messy
// and I didn't want it inline with the other code.
return populatedAccountModel;
{
public bool IsValidAccount(XmlNode node)
{
var taxSelectValue = node.SelectSingleNode("//FORMAT/Field[#taxSelect='1']").First().Value;
var accountStatus = // similar to first line in that it gets a single node using a specific XPath
var maturityDate = // similar to first line in that it gets a single node using a specific XPath
var maturityValue = // similar to first line in that it gets a single node using a specific XPath
return taxSelectValue != string.Empty && taxSelectValue != "0" && (accountStatusValue != "CL" || (maturityDate.Year >= DateTime.Now.AddYears(-1).Year));
}
}
What I want to do is test my GetAccounts() method. I can stub out the IExternalWebServiceProxy call and return fake XML, but I have internal calls happening in my service since my GetAccounts() method calls MapAccountFromXml() which in turn calls IsValidAccount().
Perhaps the solution is to not worry about breaking out the long and involved MapAccountFromXml() and IsValidAccount() code and just put them inline into the GetAccount() call, but I would rather leave them broken out for code readability.
I have my Moles assembly created, and know I can create a stub version of my class like this
var stubWebService = SIExternalWebServiceProxy {
GetAllCustomerAccounts = delegate {
return SomeHelper.GetFakeXmlDocument();
}
}
var stubAccountService = new SAccountService() { callsBase = true; }
My problem is I don't know how to then override the internal calls to MapAccountFromXml and IsValidAccount and I don't want my Unit Test to be testing thos methods, I'd like to isolate GetAccounts for the test. I read somewhere the methods need to be virtual to be overriden in a partial stub, but could not find anything that then showed how to create a stub that overrides a few methods while calling the base for the one I want to test.
Peer put me on the right track, thank you.
It turned out that what I was looking for is called Detours in Moles. Rather than stub an interface using
var stubAccountService = new SIAccountService();
what I needed to do was create an instance of my AccountService and then detour all calls to the methods I wanted to mock, like this
var accountService = new AccountService();
MAccountService.AllInstances.MapAccountFromXmlXmlNode = delegate {
return new AccountModel();
};
The MAccountService is provided by Moles when you Mole your assembly. The only missing piece to this is that for this to work you need to add the following attribute to your test method:
[HostType("Moles")]
This worked for me locally, but in the end I had trouble getting TFS to do automated builds
UPDATE
I just stumbled on another way of doing this, while looking at Rhino Mocks. If the methods in the class being mocked are virtual then you can override them in the mock, like this:
var accountService = new SAccountService();
accountService.MapAccountFromXmlXmlNode = delegate
{
return new AccountModel();
}
Now I can call
accountService.GetMemberAccounts();
and when accountService makes its call to MapAccountFromXml it will be caught by the stub and processed as I deem necessary. No messing with HostType and it works like a charm.
To test methods in you class in issolation you do this with moles by making a mole for the IsValidAccount and MapAccountFromXml methods. Or make a stub implementation with stubs where you let the stub call the orriginal methode using base. Or what I think is a nicer solution, make a test class which overrides the methods you do want to stub (this is the same what a stub would do, except you see all what is happening in your own code):
public class TestHelperAccountService : AccountService {
public override AccountModel MapAccountFromXml(XmlNode node) {
return new AccountModel(){
//Accountmodelstub
};
{
public override bool IsValidAccount(XmlNode node)
{
return true;
}
}
This way you can do your test for the GetAccount method on your TestHelperAccountService class where you GetAccount method runs in full issolation. You can do the same for the methods like MapAccountFromXml to test them seperatly.