9

Let's say i want to unit test a function in typescript. This function use an "option" type (object) parameter with a complex shape.

interface option {
  param1 : string
  param2 : number
  param3 : { 
    param4 : string
    param5 : boolean
    }
  .
  .
  .
  param15 : string
}

const functionToTest = (opt:option)=>{
   ...do stuff with option
}

Now let say i want to write a unit test that mimic the correct behaviour of functionToTest when param1 change, ignoring the other parameter that have no influence on the result.

It's my understanding that to make my test more resilient, i can just write in plain JS

const option1 = {
  param1 : "foo"
}

and do my testing

expect(functionToTest(option1)).to.be.true;

However, if i write my test with typescript, i will have to write a full option1 object with all of the interface (dummy) members, althought most will be ignored, and it will divert the reader from the true goal of the test.

Are there workarounds around this or something i should change in my thinking ?

Vincent J
  • 761
  • 1
  • 5
  • 22

4 Answers4

8

You can take advantage of typescript's Partial for this.

interface YourInterface {
  prop: string;
  foo: number;
}

const data: Partial<YourInterface> = {   
   prop: 'value'
};

// service is an instance of SomeService
service.sendData(<YourInterface> data);

class SomeService {
  sendData(data: YourInterface) {

  }
}

Rui Marques
  • 8,567
  • 3
  • 60
  • 91
  • 3
    I don't see how this would work, given that you are creating a new interface with Partial, where the question is in regards to using the existing class, but fewer properties. The TS compiler will just complain that you are missing properties. – godhar Aug 19 '20 at 09:36
  • Have you tried it? That is exactly what Partial does and why it was created. – Rui Marques Aug 19 '20 at 15:58
  • 3
    But then you use ` data` to [type assert](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions) it back to `YourInterface`, so you might as well just have done `const data = { ... } as YourInterface` to start with. – jonrsharpe Oct 29 '21 at 14:55
  • I guess it is not the same the as syntax does not work sometimes because ts does not find sufficient overlap (hence you need 'as unknown as YourInterface'), but when you explicitly define it as Partial, the later typecasting with as YourInterface works fine – gaurav5430 Mar 13 '23 at 08:26
  • Note that this only works one level deep by default, there are ways to have a DeepPartial in ts as mentioned here: https://stackoverflow.com/questions/61132262/typescript-deep-partial – gaurav5430 Mar 13 '23 at 08:27
3

In my tests, I use the as type assertion to pass in partial objects to a function.

const options = {
   param1: 'param1',
   param2: 22 
} as Option;

expect(functionToTest(options)).to.be.true;
Kristian Roebuck
  • 3,209
  • 2
  • 21
  • 29
0

you may declare params as optional.

   interface option {
    param1 : string
    param2? : number
    param3? : { 
       param4 : string
       param5 : boolean
    }
    .
   param15? : string
 }


 
Madhu Ranjan
  • 17,334
  • 7
  • 60
  • 69
  • 1
    Yeah, i had thought about that, but there are multiple scenarios where a param is not optional for the correct work of the full (integrated) software but not useful for the small part of the unit test One example : let say i have a redux store, i have defined a state interface with the full data tree, i want to unit test a function that queries the state, for unit test it doesn't bother with the whole state tree (and i don't want to have to write a full dummy state object for each state), just the part he is gonna filter Does that mean that i should write a custom typing for each function? – Vincent J May 20 '16 at 21:11
  • 1
    yes i think so , you have to write custom typing for each function, although you may declare it in the function also, you don't have to write a separate Interface for that, if that is your worry, so you may use **function hello(param: { param1: string, param2: number })** Hope this helps! – Madhu Ranjan May 20 '16 at 21:20
  • @VincentJ I agree with you, we never want to make our class props optional just to unit test them, that is ridiculous. – godhar Aug 19 '20 at 09:33
0

I'm using the Parameters Typescript utility to get the type directly from the function I'm testing:

const toBeTested = (opts) => { ... };

type MockParams = Parameters<typeof toBeTested>[0];

it('should work', () => {
  expect(toBeTested(foo as MockParams)).toBe(true);
});
Nico Prat
  • 686
  • 2
  • 6
  • 13