-
-
Notifications
You must be signed in to change notification settings - Fork 170
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
How to test components using getModule with vue-test-util #112
Comments
Did you ever figure anything out? The syntax/usage of dynamic modules is terrific, but there is 0 info on how to test (not even tests in the "real world" examples) |
@pjo336 I didn't. I was able to get my store running inside of my tests so I could commit dummy data to the mutations. This isn't ideal and it'd be much better to be able to stub the store methods completely. |
If you commit dummy data like @dalecarslaw does, it remains, even if test case is finished. |
Any updates on this? running into the same issue and I would rather not use store functionality to test my component behavior... |
I got mine to work by using |
Can you show an example test? Never even had heard of that method |
Here is an example of using
|
sorry for the delay! been a bit busy :/ so lets assume you use get fooModule() {
return getModule(FooModule, this.$store)
} then all you have to do is override const wrapper = shallowMount(FooComponent, {
computed: {
fooModule: () => ({}),
},
localVue,
}); You can also return your own mock version of the store if you like. personally I don't like testing store logic in my components so I override it with an empty object, and mock the other getters that I use to access the store getters, and methods used to access the store mutations and actions. |
The alternative to this pattern is let's say you have a singleton: // foo.ts
export default getModule(Foo, store); ...which you call in your component: import Foo from './foo';
export default class Component extends Vue {
doSomething() {
let someArg;
// Complex logic
Foo.doSomething(someArg);
}
} ...then in tests you could stub the singleton: describe('Component', () => {
let component;
beforeEach(() => {
sinon.stub(Foo, 'doSomething').callsFake(() => {});
component = shallowMount(Component);
});
afterEach(() => {
sinon.restore();
});
it('does something complex', () => {
component.vm.doSomething();
expect(Foo.doSomething).to.have.been.calledWith('abc');
});
}); |
@alecgibson I was very excited to test your proposal, since that is exactly my pattern. But sadly this didnot work for me :( sinon.stub(..) is not stubbing/mocking my StoreModule and the test is still suing the original StoreModule :( @jubairsaidi I dont have the computed property in my mock options... :( |
@souphuhn I've since changed away from this pattern. We're now adding the store singleton to the Vue prototype: Vue.prototype.$foo = getModule(Foo, store); ...which means that in the components, you can just mock it using mocks: const component = shallowMount(Component, {
$foo: {doSomething: sinon.spy()},
}); |
@alecgibson Thank you so much for the fast reply. Firstly, your approach of Secondly I am still struggling of mocking this global property const mocks = { $foo: myMockedFoo };
const component = shallowMount(Component, {mocks}); My component is accessing get store() : FooModule {
return this.$foo;
get someData (): any {
return this.store.someData;
} In this way, my component is accessing correctly const myMockedFoo = {
state: {
someData: 'mockedValue'
}
} |
Shouldn't you have: const myMockedFoo = {
someData: 'mockedValue',
}; ? Pretty sure this would become obvious if you inspect the runtime value of |
@alecgibson Thank you! It works like a charm |
Oh man, I love this pattern, I was using
which made my code untestable. Now I'm using:
And it works great. |
My tests are working with this pattern, but something is still not correct the way I register the store, so that when I serve my application I get that $overview is undefined, here is my store import Vue from "vue";
import Vuex from "vuex";
import { OverviewState } from "./overview-module";
Vue.use(Vuex);
export interface RootState {
overview: OverviewState;
}
// Declare empty store first, dynamically register all modules later.
export default new Vuex.Store<RootState>({}); Here is my module: export interface OverviewState {
items: GlossaryEntry[];
filter: OverviewFilterOptions;
}
@Module({ dynamic: true, store, name: "overview" })
export class OverviewModule extends VuexModule implements OverviewState {
public items: GlossaryEntry[] = [];
public filter: OverviewFilterOptions = {
contains: "",
page: 0,
pageSize: 20,
desc: false
};
@Mutation
private async UPDATE_OVERVIEW(filter: OverviewFilterOptions) {
this.filter = Object.assign({}, filter);
await overviewService
.filter(this.filter)
.then((response: Response<Overview>) => {
this.items = Object.assign({}, response.data.items);
});
}
@Action
public async updateOverview(filter: OverviewFilterOptions) {
this.UPDATE_OVERVIEW(filter);
}
}
Vue.prototype.$overview = getModule(OverviewModule); Could please someone help? |
@dgroh it looks like you have a circular dependency here? You import I suspect if you remove the |
This was a good point. It "works" now, but when I use private updateOverview(value: string) {
this.$overview.updateOverview({ contains: value }); // this breaks my entire app
} This I use |
Is there still not a better solution or any documentation related to this ? |
What works best for me: Assuming you have a dynamic module declared as follows: // @/store/modules/my-module.ts
import { getModule, Module, VuexModule } from "vuex-module-decorators";
import store from "@/store";
@Module({ name: "myModule", store, dynamic: true})
export class MyModule extends VuexModule {
// state, getters, mutations, actions
}
export const myModule = getModule(MyModule); Importing this module in a component as follows // @/components/MyComponent.vue
import { myModule } from "@/store/modules/my-module"; In a test I set up mocks as following // @/components/__tests__/MyComponent.spec.ts
import { mocked } from "ts-jest";
import { myModule } from "@/store/modules/my-module";
jest.mock("@/store/modules/my-module")
// Provide mock typing. This does seem to wrongly assume that getters have also been mocked by jest.mock, but it does work nicely for actions and mutations
const myModuleMock = mocked(myModule);
// All mutations and actions are now mocked with default `jest.fn` (which just returns `undefined`)
// Mocking state (you should probably only access state through getters though)
myModuleMock.someStateProp = //Whatever I want to mock as a value
// Mocking a getter, Jest does not mock getters. @ts-expect-error required for suppresing TS2540 (can't assign to read-only prop)
// @ts-expect-error
myModuleMock.someGetter = //whatever I want to mock as a value
// Mocking a getter that returns a function
// @ts-expect-error
myModuleMock.someGetter =() => { /* whatever I want to mock as a return value */ }
// Mocking a mutation for test suite
myModuleMock.someMutation.mockImplementation(() => /* Some implementation */)
// Mocking a mutation with a specific implementation in one test
myModuleMock.someMutation.mockImplementationOnce(() => /* Some implementation */)
// Mocking an action for test suite, best to use promises as actions are async
myModuleMock.someAction.mockImplementation(() => Promise.resolve())
// Mocking an action with a specific implementation in one test
myModuleMock.someAction.mockImplementationOnce(() => Promise.resolve()) |
@Robin-Hoodie I tried to use this way but the getter keeps returning undefined to the component and then it gives an error because it tries to access a getter property and the getter is undefined. |
@Robin-Hoodie Thank you it's been a week i've been struggling with this lib! Also, for people in future reading this and wanting to make an app with this lib and having call to actions/mutations/getters in other modules, don't try to do it with a static modules, it doesn't work. Make your store with dynamic modules and test it the way Robin Hoodie does it. Also, here are some interesting links that helped me: |
How would you go about stubbing actions and other methods inside of your store when using the getModule functionality inside of a component for testing?
MyComponent.vue
ExampleStore.ts
Test
In the above example I would need to stub the fetchSomeData action
The text was updated successfully, but these errors were encountered: