If I component that has a large configuration object that is intended to be used by several internal services, but an individual service may only be interested in one or two properties in this config... How do I deal with testing this?
In my test, I was trying to just pass in a Partial<MyLargeConfig>
with only the relevant properties set, but I get errors about a property being optional for the partial, but required in the actual interface.
How can I get around this without having to have my tests declare some obnoxious config object with all the properties set? Is there any way to tell typescript to allow a partial in the context of my tests only?
I would like to avoid having to add ?
to the end of all the properties... That seems like the wrong solution. Declaring all the properties in the test also seems like the wrong solution.
*** EDIT ***
To make this less abstract (yet still somewhat abstract), what I am doing is, building a configuration object and passing it to a component such as:
interface MyLargeConfig {
a: string,
b: number,
c: boolean,
d: () => void,
e: ...etc
}
<my-component [config]="config"></my-component>
and then the component internally would be doing
this.serviceA = new InteralService(this.config); // only makes use of properties a & c in the config
this.serviceB = new OtherInteralService(this.config); // only makes use of properties b & d in the config
this.serviceC = new YetAnotherInteralService(this.config); // // only makes use of properties c & f in the config
CodePudding user response:
Your tests need to conform to the types that are in your production code. Otherwise, you could be missing all sorts of bugs. And you really do need all the config properties in this case because those internal services will get instantiated whether you care about testing them our not.
To make that less cumbersome, I would probably setup some functions to generate fixture data.
function configPropsFixture(values?: Partial<MyLargeConfig>): MyLargeConfig {
return {
a: 'foo',
b: 123,
c: true,
d: () => undefined,
...values
}
}
That function sets up some typical defaults, and allows you to pass in an object which overrides any of those defaults.
For example:
function myComponent(config: MyLargeConfig) {
//...
}
// some tests test
const result = myComponent(configPropsFixture())
const resultA = myComponent(configPropsFixture({ a: 'bar' }))
const resultB = myComponent(configPropsFixture({ b: 456 }))
This conforms to the type contract from your component, while also making it really simple to test specific values on specific props.
Also, I don't know how InteralService
and it's ilk define their arguments, but those should be typed as a subset of the large config:
class InteralService {
constructor(args: Pick<MyLargeConfig, 'a' | 'b'>) {}
}
class OtherInteralService {
constructor(args: Pick<MyLargeConfig, 'c' | 'd'>) {}
}
Or perhaps:
interface InteralServiceProps {
a: string,
b: number,
}
class InteralService {
constructor(args: InteralServiceProps) {}
}
interface OtherInteralServiceProps {
c: boolean,
d: () => void,
}
class OtherInteralService {
constructor(args: OtherInteralServiceProps) {}
}
interface MyLargeConfig extends InteralServiceProps, OtherInteralServiceProps {
e: string // other props here?
}
You can always pass in more properties than a type expects, so this should work fine. And then when unit testing those inner services you only need to pass in just the props that those services require.