Reputation: 9552
I have a TypeScript +2.4 project where I'm using Jest for my unit tests. The project has a lot of poco models, without a default value. For example:
export class Foo {
public id: number
public name: string;
public when: Date;
}
Each of these models is mapped from raw json to this class. It is a requirement for my tests that all properties are assigned, e.g. have values. This leads to the following test that has to be written for all models:
test('Foo() should have its properties assigned', () => {
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
};
// manually assert each propertie here
expect(target.id).toBeDefined();
expect(target.name).toBeDefined();
expect(target.when).toBeDefined();
}
To me, that's not so DRY to do for each test. Not to mention error prone and cumbersome. What I would like to do is create a helper that iterates through each property and asserts that it has a value assigned.
Example 1 - Object.keys
This example is incorrect because Object.keys only iterates through the already assigned properties, ignoring the non-set properties (and thus always is positive):
public static AssertAllPropertiesAreAssigned(target: object): void {
Object.keys(target).forEach((key, index) => {
expect(target[key]).toBeDefined();
});
Example 2 - Object.getOwnPropertyNames()
The same as example 1:
public static AssertAllPropertiesAreAssigned(target: object): void {
Object.getOwnPropertyNames(target).forEach((name, index) => {
expect(target[name]).toBeDefined();
});
Example 3 - Set default values
By assigning a default value to each poco, like null
, I can make the earlier samples work. But I'd sure like to avoid that at all cost:
export class Foo {
public id: number = null;
public name: string = null;
public when: Date = null;
}
The question: is there a way to create a helper that asserts that each property of my TypeScript poco object is actually assigned a value, in my test? Or, as an alternative, does Jest have some util for this?
There are similar questions on SO, but they are not related to asserting the values in a test. This makes this question, as far as I've looked around, differ from the others:
Also, I'm aware that the Javascript compiled output of my poco will probably leads to that the unset properties are simply not available:
var Foo = (function() {
// nothing here...
}());
But with TypeScript's strong typing power and recent changes and/or Jest helpers, there might be some additional options to get this done?
Upvotes: 0
Views: 1866
Reputation: 329168
Most of your options aren't any better than the answers to those other questions: initialize the properties (good idea); use property decorators (tedious).
Personally, I think it should be an error to declare a class property as a can't-be-undefined type like string
and then not define it in the constructor, but that feature isn't part of TypeScript yet, even if you turn on strictNullChecks
(which you should). I don't know why you don't want to initialize the variables, but this would work:
export class Foo {
public id: number | undefined = void 0;
public name: string | undefined = void 0;
public when: Date | undefined = void 0;
}
Now an instance of Foo
will have the relevant keys if you do Object.keys()
even though the values will still be undefined
.
Wait a minute, you're not even using the class at runtime:
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
}; // object literal, not constructed class instance
console.log(target instanceof Foo) // false
Then I suggest you use an interface
instead of a class
, and just turn on strictNullChecks
:
export interface Foo {
id: number;
name: string;
when: Date;
}
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
};
const badTarget: Foo = {
id: 1002;
}; // error, Property 'name' is missing
Now TypeScript will not let you assign a possibly-undefined value to those properties and you don't have to bother looping over anything at runtime.
Hope that helps!
Upvotes: 1