Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object attributes are being copied instead of referenced #981

Open
Philipp3211 opened this issue Nov 10, 2021 · 3 comments
Open

Object attributes are being copied instead of referenced #981

Philipp3211 opened this issue Nov 10, 2021 · 3 comments

Comments

@Philipp3211
Copy link

When object attributes are given as parameter to the createMock function, they are copied instead of referenced. This is unexpected and has the disadvantage, that changes on the original object are not reflected in the mock:

interface TestInterface {
    testAttribute: {
        id: number
    };
}

const test = {
    id: 0
};

const testMock = createMock<TestInterface>({testAttribute: test});

test.id = 1;

const finalTest = testMock.testAttribute.id === test.id; // expected true, but is actually false
@Pmyl
Copy link
Collaborator

Pmyl commented Nov 10, 2021

This is by design, provided overrides are merged recursively onto the created mock.
In general this is the standard way of doing it, let's look at your example:

const testMock = createMock<TestInterface>({testAttribute: test});

What does this mean? If you want the objects to be provided by reference it means that the whole { testAttribute: test } will be assigned to the mock, hence making it the effective mock. This means that there is no need to use createMock and this is enough:

const testMock = { testAttribute: test };

There is a solution, that is to check if the object provided has the same properties than the mock object and provide by reference only if all the properties are provided... but this becomes very inefficient if we start working with big deep objects.
A quick example is an interface like this:

interface INode {
  child?: INode;
  name: string;
}

createMock<INode>({ name: 'root', child: { child: { child: { child: { name: 'a name' } } } } });

Is root here provided by reference? Why not? Checking that would make the code slower than it should be and we would probably get more issues like this one where someone else didn't want the object to be provided by reference.

If this is a common feature request I can think of a solution, in the meantime in the next part I'll explain two ways of achieving this without having to add any feature to ts-auto-mock


One way of achieving this is to follow the documentation that says that if the provided override is a mock then it's provided by reference, another is a simple solution that just adds one line of code to your example.

So what you can do is:

interface TestInterface {
    testAttribute: {
        id: number
    };
}

const test = createMock<{ id: number }>({ id: 0 });
const testMock = createMock<TestInterface>({testAttribute: test});
test.id = 1;

const finalTest = testMock.testAttribute.id === test.id; // true

or without having to resort to hacks:

interface TestInterface {
    testAttribute: {
        id: number
    };
}

const test = {
    id: 0
};
const testMock = createMock<TestInterface>();
testMock.test = test;

test.id = 1;

const finalTest = testMock.testAttribute.id === test.id; // true

Hope this helps :D

@Philipp3211
Copy link
Author

Philipp3211 commented Nov 10, 2021

Checking that would make the code slower than it should be

Yes, features do make code slower. The question is, does this decrease in performance actually make a difference?

(...) we would probably get more issues like this one where someone else didn't want the object to be provided by reference.

As Typescript objects get assigned by reference per default, I feel like the current behaviour where objects get copied - given they are not mocks themselves - is rather counter-intuitive more so than intuitive.

@Philipp3211
Copy link
Author

Philipp3211 commented Nov 10, 2021

Just an idea here: Is it possible to check if attribute values of the passed object are variables rather than literals?

Oh well now that I think about it, if the attributes are given by a literal or a variable doesn't really determine if it's partial or not, because you can also put the partial assignment into a variable first, before calling createMock.

What you could maybe do is checking the prototype chain of objects. If they are instances of the respective type you could reference them instead of copying, but this would only work with classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants