I'm creating an app using React, Flux, and TypeScript. I'm testing using Karma and Jasmine.
I have a Store, like this:
class MemberStore {
private _members:Member[];
public get members():Member[] { return this._members; }
}
As with all Flux stores, data is read-only and modified through action handlers (not shown), not setters.
I would like to mock this data store in a unit test of code that depends on MemberStore
. However, since members
is read-only I cannot do something like this:
var mockMemberStore:MemberStore = jasmine.createSpyObj("MemberStore");
mockMemberStore.members = [/*mock members*/];
The above code actually emits working JS, since mockMemberStore
is not actually an instance of MemberStore
but a Jasmine spy object (and createSpyObj
returns any
). However, it generates a compile-error on mockMemberStore.members = []
because it's a read-only property.
EDIT: So it turns out I was mis-reading the error, and it was not this code that gave an error. It was not a compile error, it's a runtime error. Surprisingly, TSC doesn't care if I assign a value to
members
at compile time, even though it's read-only. In the above example I replaceMemberStore
completely with a jasmine SpyObj, which works. If instead I simply try tospyOn
a real version ofMemberStore
, that's when I see a runtime error thatmembers
cannot be assigned. Example
I could change it to var mockMemberStore:any
, or use casting like (<any> mockMemberStore).members = []
, but then the compiler doesn't see members
as a reference to ModelStore/members
so all static type checking is lost (and things like "find references" and "refactor/rename" don't work). What is the approach to use here?
You should be able to write:
(mockMemberStore as any).members = [""];
This will not change the type of any future references to mockMemberStore
. Because this is a unit test, the code does not need to be as perfect as it usually is. The important thing is that it will fail if you introduce an error, and in this case, it will fail: if you perform an invalid refactor, renamedMembers
will no longer be set and the test should blow up, and you will be able to fix it.
This is not ideal, since you do lose find-usages, but it's not as bad as it often is in other languages (mocking readonly objects is notoriously cumbersome).
Interfaces are useful for this:
interface IMemberStore {
members: Member[];
}
class MemberStore implements IMemberStore {
private _members:Member[];
public get members():Member[] { return this._members; }
}
class MockMemberStore implements IMemberStore {
members: Member[];
}
Use IMemberStore
in most of your application, use MemberStore
as the implementation, and use MockMemberStore
in your unit tests.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.