I'm reading through the Vue Testing Cookbook and Vue Test Utils's docs, where they touch on testing components with Vuex. Both sources advise using createLocalVue, but I don't fully understand why. We already have a couple of tests that use Vuex but don't use createLocalVue, and they work. So why do these sources suggest using createLocalVue?
Here's the test that appears to be a bad practice according to those sources. Is this code going to break something down the road? Is it causing unwanted side-effects we're not aware of?
import { mount } from '#vue/test-utils';
import Vuex from 'vuex';
import Component from 'somewhere';
// We don't use this and yet the code seems to work
// const localVue = createLocalVue()
// localVue.use(Vuex)
describe('Foo Component', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(Component, {
// localVue, <-- We don't use this
store: new Vuex.Store({
modules: {
app: {
namespaced: true,
// ... more store stuff
}
}
})
})
});
it('should contain foo', () => {
expect(wrapper.contains('.foo')).toBe(true);
});
});
From docs:
localVue: A local copy of Vue created by createLocalVue to use when mounting the component. Installing plugins on this copy of Vue prevents polluting the original Vue copy.
In your tests, you might want to make particular changes and install plugins on the tested Vue instance. Using localVue ensures those changes are reset for every test.
An obvious advantage is you don't have to install all plugins for all tests. So your tests will be faster.
Also, when your tests get a bit more complex, if you don't use localVue you'll experience tests failing erratically based on their order, because the previously ran test, even though it passed, modified the Vue instance in a way that breaks your next test. But the next test passes when ran in isolation.
The type of errors which typically imply hair loss.
Using localVue provides a type of certainty most developers welcome when running tests. If you feel adventurous, don't use it.
It's a recommendation, not an imposition.
Related
I'm having an exciting upgrade in a vue2 project using #vue/composition-api 0.6.7, trying to upgrade to 1.7.1.
Some tests are breaking and I'm noticing a pattern where the tests in question mount a component with the data parameter, as if they're reaching in and manipulating a ref
Example
// MyComponent.vue
<script>
export default defineComponent({
setup() {
const showModal = ref(false)
return {showModal}
}
})
</script>
Example test which was working before but now is broken.
it('should show the modal when the showModal ref is true', () => {
const wrapper = mount(MyComponent, {
data: {
showModal: true
}
});
expect(wrapper.find('#modal').exists()).toBe(true)
}
This makes sense to me that this broke in some ways because data is more of an options api thing and creating a ref is more of a composition-api thing-- probably more solidified as we went to 1.0 in the composition-api. That said, do you think that it should work?
When I rewrite the tests to NOT mount using the data prop and test it another way, the tests pass fine. I was expecting it to work as before and honestly not testing using the mount({data}), wrapper.setData(), or wrapper.vm seems like a better approach. I'm just looking for a confirmation or root cause why it worked before and not now.
Beyond this condensed code sample, I have tests using wrapper.setData({ serverResponse }) to simulate when a network call returns. This similarly breaks when I upgrade this composition-api package.
I see two ways of mocking services in NestJS for unit testing, the first one is the same as we define providers in real modules like:
const module = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: mockUsersRepository,
}
],
}).compile();
And the other way with overrideProvider method. As following:
const module = await Test.createTestingModule({
imports: [UserModule]
})
.overrideProvider(getRepositoryToken(User))
.useValue(mockUsersRepository)
.compile();
What is the difference?
So let me try to explain it this way:
overrideProvider is useful when you've imported an entire module and need to override something it has as a provider. A use case, like the answer mentioned, would be overriding a logger. So say you have
const modRef = await Test.createTestingModule({
import: [AuthModule]
}).compile();
And assume that AuthModule has imports: [ LoggerModule ]. In our test, we don't really want to see all the logs created, but we can't provide a custom provider for the LoggerService because it's being imported and used via the LoggerModule (overriding an injection token isn't really a common practice). So to provide our own implementation forLoggerService (let's say we just need a noop log method) we can do the following
const modRef = await Test.createTestingModule({
import: [AuthModule]
})
.overrideProvider(LoggerService)
.useValue({ log: () => { /* noop */ } })
.compile();
And now when our AuthService calls this.logger.log() it will just call this noop and be done with it.
On the flip side, if we're doing unit testing, usually you don't need to overrideProvider because you just set up the provider and the custom provider directly in the testing module's metadata and use that.
The overrideProvider is really useful when you have to use imports (like integration and e2e tests), otherwise, generally, it's better to use a custom provider
The difference is pretty simple.
With the first approach (array of providers), you create custom testing module to test (probably) the UserService.
With second approach, you use complete module in the very same shape as it is used in the application itself.
The result is exactly the same - your mock is being injected into the constructor of UserService.
The first approach is better for small, mostly unit tests, but these tests can be also done without using NestJS test tooling at all (just pass mock manually to the ctor), while the second one does a great job in integration tests.
Repository is not great example to use to explain, but think about Logger.
You are performing some integration tests of 2 or more modules.
You do not want to manually create big testing module (which also is breaking the connection with real shape of your modules), but you want to just import your modules which are being tested together and .overrideProvider for Logger with e.g. loggerMock which lets you to assert all logger calls across all tested modules.
Example:
#Module({providers: [LoggerService], exports: [LoggerService]})
export class LoggerModule {}
#Module({imports: [LoggerModule], providers: [FooService]})
export class FooModule {}
#Module({imports: [LoggerModule], providers: [BarService]})
export class BarModule {}
#Module({imports: [FooModule, BarModule]}
export class AppModule {}
// TEST
const testModule = await Test.createTestingModule({
import: [AppModule]
})
.overrideProvider(LoggerService)
.useValue(/* your logger mock will be provided in both FooService and BarService and you can easily test all related to logs then */)
.compile();
I hope it is clear. If not, please leave a comment and I will try to explain more.
I have a function in angular 2 service which I would like to test.
service.ts
upload(){
let file = new Transfer();
file.upload(myfile).then( // my callback );
}
I would like to mock Transfer in my test using jasmine. I tried this in my
sevice.spec.ts
import { TransferMock as Transfer } from '../mocks/mocks' to mock it. But it is not working. This is how my test is instantiated .
describe('authentication service' , () => {
beforeEach(() => {
auth = new Service(<any>new HttpMock(), <any>new StorageMock())
});
it('initialize authentication',() => {
expect(auth).not.toEqual(null);
auth.upload('file'); //it fails here
});
})
edit
Transfer is not injected in the service. Only one function uses Transfer . So not injecting can reduce the initial loading time of the app i guess(would be happy to know other opinions) . So I would like to know if there is anyway to mock if its constructed this way ?
edit
Although I had accepted Martin's answer as it is the best practice, it has one issue which can happen when you use ionic-native plugins.If the plugin doesnt have browser support it can fail. In this case it happened when I inject it, with error FileTransfer is not defined . So I am back again, looking for suggestions.
In order to provide a mock for a class in a test, you need to inject the class in your implementation.
In your ngModule add Transfer to your providers. Then simply inject it into your service.
Then in your test you can use { provide: Transfer, useClass: TransferMock } in your TestBed providers.
Update
The primary purpose of Dependency Injection is to make code testable and to allow mocking - faking - stubbing of services.
Update
With Dependancy Injection you can configure a different set of providers for different environments.
For example, if you are running your application in the browser, and in a native mobile environment you can swap out your configuration.
In your module you could have something like this:
const TRANSFER_PROVIDER: any;
if (environment.browser) {
TRANSFER_PROVIDER = Transfer;
} else {
TRANSFER_PROVIDER = { provide: Transfer, useClass: NativeTransfer }
}
...
providers: [ TRANSFER_PROVIDER ]
NativeTransfer could be a simple stub that does nothing but prevent errors, or it could let the user know that this feature is not supported in their browser.
My app stores some information about the current session in localStorage. Therefore I need my tests to clear localStorage before or after each single test throughout all test files. Is there a way to define a beforeEach or afterEach callback globally instead of on each test file?
We had wrapped ember-qunit's module, moduleFor and moduleForComponent for a nearly the same reason. And we are importing those wrappers instead of ember-qunit.
Another suggestion is to wrap localStorage with a service. Never access to localStorage except this service. So you can use a mock implementation of it in tests.
Updated:
How it is realised:
import { moduleFor, moduleForModel, test, only, setResolver } from 'ember-qunit';
import { moduleForComponent as qunitModuleForComponent } from 'ember-qunit';
function moduleForComponent(name, description, callbacks) {
//our implementation that wraps "qunitModuleForComponent"
//eg. init commonly used services vs.
}
export {
moduleFor,
moduleForComponent,
moduleForModel,
test,
only,
setResolver
};
Pros:
Prevents code duplication
Centralize unit test management
Easy to add new methods for custom needs, such as: moduleForValidatableComponent, moduleForLocalStorage
Cons:
ember-cli generates tests those are importing ember-qunit. Developers must change the import statements to these wrappers. It was sometimes forgotten. (When a test fails, developers remember that they need to change import statements.)
For some tests, wrapping is unnecessary.
TLDR
I'm trying to unit test a very simple component. However, it appears some very common test helpers aren't being defined. This is something specific about unit-testing a component, as I'm using these in integration tests without issue.
Now just jump straight to the Questions at the end.
Details
The errors are generic:
click is not defined
andThen is not defined
Stack trace for context:
Died on test #4 at Object.test (http://localhost:7357/assets/test-support.js:110:11)
at http://localhost:7357/assets/skylab.js:14977:15
at mod.state (http://localhost:7357/assets/vendor.js:150:29)
at tryFinally (http://localhost:7357/assets/vendor.js:30:14)
at requireModule (http://localhost:7357/assets/vendor.js:148:5)
at Object.TestLoader.require (http://localhost:7357/assets/test-loader.js:29:9)
at Object.TestLoader.loadModules (http://localhost:7357/assets/test-loader.js:21:18): click is not defined
The component and the tests are very basic. The component:
import Ember from 'ember';
export default Ember.TextField.extend({
classNames: ['input-span']
});
The Test:
import Ember from 'ember';
import {
moduleForComponent,
test
} from 'ember-qunit';
moduleForComponent('custom-input');
test('focus on click', function(assert) {
assert.expect(1);
var component = this.subject();
this.render();
click('input')
assert.ok(component.$('input').is(':focus'));
});
Best Guess
My best guess is that these helpers work in the acceptance tests because the startApp helper creates the click and andThen helper functions. I don't have setup & teardown code in my moduleForComponent call, but it doesn't look like I should need it. And I don't want to test the whole app here -- just an isolated component.
Questions
Is there another way to inject these test helpers that I'm unaware of?
Am I writing these tests wrong? Should I never use click in a component test? Is the documentation simply outdated?
Should this be supported as-written, and is this a framework bug I should report?
acceptance level helpers currently depend on an app being spun up, as such they are not available for unit level tests. As those do not have an app.