Closed JuanCaicedo closed 2 years ago
I know it would be possible to fix the TS compilation on dog1
by adding an age
, but in my code that's specifically what I'm trying to avoid π The object I want to fake is a type from a third party and I would need to define a ton of things in order to satisfy the type
The literal reason you're seeing this is probably because td.object
generates functions, not static values, of the names passed to it.
In ~13 years (yikes) of practicing TDD on-and-off-again with mocks, I have become pretty fixed in my conviction that values (stuff with name
and age
like your Dog
) should have only elucidative behavior that describes the data they hold, and not any actual feature/business logic. As a result, I never mock them, I always instantiate real ones in my tests. If it's too hard to instantiate those values, then it usually means they're too complicated and I take that feedback to simplify or break down. So for that reason, I'd recommend just instantiating a dog and passing it.
Second, I only fake dependencies that expose some kind of feature/business functions that do the work needed by the subject under test. That means I'd be more likely to replace a module that had a blowDry(dog)
function that either transformed or mutated the dog and stub its return value or verify it was called, respectively.
Hi Justin! Thanks for the guidance π
Do you think this API would make sense? If so I think that would unlock my use case, even if it's not the best practice π
const partialDog: Partial<Dog> = {
name: 'Spot'
}
const mockDog = td.object<Dog>(partialDog)
mockDog.name // 'Spot'
mockDog.age // undefined
In case that's not possible, I'll add a few questions on the rest of your answer π Thanks!
The impression I get is the above already works at runtime. If that seems okay, then perhaps it would be possible to change main/index.d.ts#L224-L232 to the following
/**
* Create a fake object that is deep copy of the given object.
*
* @export
* @template T
* @param {Partial<T>} object Object to copy.
* @returns {DoubledObject<T>}
*/
export function object<T>(object: Partial<T>): DoubledObject<T>;
Let me know what you think of that π I can try it against my tests to see how it would work
This might technically work, but I'm still fuzzy on the problem being solved here. As far as I can tell there are at least two issues:
Faking a value object using td.object
that contains some number of static properties that must be set for the backing typescript type to be considered valid. As I wrote above, faking value objects isn't IMO a good idea, and instead real value objects should be used whenever possible. (And to the extent that values & application behavior are inter-mingled, I'd take that as a cue to disentangle them.) As a result, I'm not particularly motivated to make any changes to support that usage
Trying to pass a partial thing to td.object
so each and every property doesn't have to be satisfied before the object is copied and a fake is returned. (I don't use Typescript, but I imagine that's what Partial<T>
would be doing, and is a built-in type of the language.) To me, this seems to violate type safety -- any time I'm creating and passing a not-quite-valid version of an object to a function, the primary benefit of a type system is the compiler telling me "yo that Dog
over there isn't valid". I would think (and when I use mock objects in typed languages, I personally practice) that I'd actually want my tests to be a way to flesh out my types, which requires that all instances be valid and complete.
I'm speculating a lot above, but am I off base here? Is td.object
in particular different from other functions in the library such that it only makes sense in this case or would you apply this to every td
function?
Hi Justin and team! Hope you're all doing well π
Description
I would like to create a dummy object that satisfies types in my test and allows me to target different cases in my tests.
Issue
Say I have a type defined
And I have a function that depends on this type
I would like to satisfy the
Dog
type in my tests, and also assert a specific behavior.These are the two ways I imagined doing that
The part about name being a
Function
really confuses me πLet me know if I'm doing something wrong, or if there's something to address in the lib that I could help out with! Thanks y'all π
Environment
node -v
output: v16.14.0npm -v
(oryarn --version
) output: 8.3.1npm ls testdouble
(oryarn list testdouble
) version: 3.16.4Repl.it Notebook
https://replit.com/@JuanCaicedo1/TDTestObjects2