Is it possible to spy on an imported function within a Vue SFC using Vitest and Vue Test Utils? - vue.js

I'm working on unit testing all of my components, and I've hit a bit of a snag (or maybe a misunderstanding of how to properly test).
I have a component that receives some parameters from the store, and then calls a series of functions that mostly return an input string with some transformations applied.
This looks something like the following:
<script setup lang="ts">
import { someTextTransform } from '../utilities/someTextTransform';
...
let parsedText = store.input;
if (store.applySomeTextTransform) {
parsedText = someTextTransform(parsedText);
}
I have already tested these functions themselves, so to me it seems like testing the actual output of the calls is unnecessary and I instead could just test to see if the functions have been called.
Here is a snippet on spying from the Vitest docs
Mocking functions can be split up into two different categories; spying & mocking.
Sometimes all you need is to validate whether or not a specific function has been called (and possibly which arguments were passed). In these cases a spy would be all we need which you can use directly with vi.spyOn().
This is exactly what I want. I want only to know whether or not the function has been called. The functions themselves are simple enough that they do not require additional setup (i.e., mocking is not required).
However looking through the docs of Vue Test Utils I don't see a way to do this. It looks like a more common way to do this is to mock the functions first and then you can spy on them more easily, but then I would have to create mocks that don't actually mock the functions which seems like I'm adding code to my codebase for no benefit other than a weird workaround.
I may just be missing something on how to do this, but I might also be approaching the testing in a suboptimal way so any advice is appreciated.

Related

How to properly test component logic in Vue.js

I'm writing a new Vue project and want to test my components with vue-test-utils.
So far I've been writing unit-tests with a test case for each component method, testing its expected behavior while mocking other methods that it might call.
Recently I've read on the Vue docs that the unit tests for components should test general behavior and not rely on implemenation details:
https://vuejs.org/guide/scaling-up/testing.html#component-testing
which makes sense, but I still want to test the logic of my component's methods.
I don't want to extract it to different files / composables because they heavily rely on the component's data and other methods, and I don't wanna pass everything as parameters.
What do you recommend regarding this?
I can't mock methods anymore since setMethods is now deprecated in vue-test-utils, which is making it harder to test each method separately.
do you think I should give up on testing each method?
I find it helpful to test methods because usually small changes make those tests fail which help me notice errors in the code, but also makes it harder because every small legitimate change requires unit-test changes as well.
We have done unit test using vue-jest. Here are the steps.
Use the following code to set up the crypto property globally. This will allow Jest to access window.crypto and won't cause any issue.Create a crypto.mock file and paste following code.
const crypto = require('crypto')
Object.defineProperty(global.self, 'crypto', {
value: {
getRandomValues: arr => crypto.randomBytes(arr.length)
}
})
Now import this file in your test file component. Now You can use jasmine.Spy to spy on the elements.
let spyFor: jasmine.Spy; spyFor = spyOn(function.hello, 'hello'); expect(spyFor).toHaveBeenCalledTimes(1);
In this way you can mock on the methods. Pls free to ask if you have anything more.

TestCafe t.eval vs. ClientFunction

What is the difference between TestCafes t.eval() function and ClientFunctions?
I've been using TestCafe for a while now and didn't stumble across the eval function until now.
I am curious, but I also have a concrete use case, which is not the subject of this question, but might help me understand the difference between both.
I'd like to do simple page manipulation tasks like hiding elements from my tests code.
The documentation clearly says that ClientFunction should not be used for this: "Do not modify the tested webpage within client functions. Use test actions to interact with the page instead."
Is the eval function suitable for this?
In essence, they are the same. The difference is that ClientFunction creates an instance that can be saved to a variable and then reused several times, while t.eval works without saving. Also, t.eval is executed immediately, while ClientFunction is not - it creates an instance that you need to call explicitly in order to run it.

How to setState in react-testing-library

Overview:
I refactoring script tests before I was used Enzyme to test, but now, I want to use #testing-library/react
Problem:
I can't find a solution for setState in #testing-library/react
Using setState is dangerous approach regardless testing library used.
It depends on implementation details(say, property names inside the state) so it becomes much harder to maintain tests - more tests to change, easy to get test broken when app is fine etc.
You cannot call that once you convert class component to functional one with hooks. So you depends on implementation details even more.
And finally direct state manipulation may end with state you would never get in real world. This means your component will be broken because it's impossible to reach some state but your tests with direct initialization will be fine.
So what you better do? Provide props, change props, call props(wrapper.find('button').filter(button => button.text() === 'Cancel').props().onClick() for enzyme, fireEvent.click(getByText(/Cancel/i)) for RTL) and verify against what's rendered.
This way your tests will be shorter, most actual and need less changes after you update component under test.

Testing without injection

Most of what I've read about mocks, stubs (test doubles) involves some form of injection of the DOC either through the SUT method itself or constructor or setter methods. And injecting that breaks boundaries like InjectMock are frowned upon as a regular test strategy. But what if you are building a class that you do not want to expose those DOCs? Is there a way to 'unit' test such a module? Without AOP? Is such a test not a real 'unit' test anymore? Is the resistance I'm feeling really design smell and I should expose those DOCs somehow?
For example, lets say I have the following Class that I want to test (unit or otherwise):
public class RemoteRepository {
Properties props = null;
public RemoteRepository(Properties props) { this.props=props; }
public Item export (String itemName) {
JSch ssh = new JSch();
ssh.setIdentity(props.get("keyfile"));
ssh.connect();
ssh.execute("export "+itemName+" "+props.get("exportFilename"));
...
}
Here is a unit I'd like to write a unit test for, but I want to stub or mock out the JSch component. But the objects I create in the method to just do things that the method needs to accomplish are not exposed outside the method even. So I cannot inject a stub to replace them. I could change the export method signature to accept the stub, or add a constructor that does, but that changes my design just to suit a test.
Although the unit will connect to a real server to do the export in prod, when just testing the unit I either want to stub the DOC out completely, or simulate it with a real DOC that is simple and controlled.
This latter approach is like using an in memory db instead of a real one in that it acts and behaves like the eventual db that will be used, but can be confined to just what is needed for the test (eg. just the tables of interest, no heavy security, etc). So I could setup some kind of test double sshd in my test so that when the build runs the test, it has something to test against. This can be a lot of trouble to setup and maintain however and seems like overkill - sometimes trying to stub out a real DOC is harder than just using the real DOC somehow.
Am I stuck trying to setup a test framework that provides an sshd test double? Am I looking at this the wrong way? Do I just use AOP or mock library methods that break the class scope boundaries?
To restate the basic problem is that a lot of times I want to test a method that has complex DOCs (ie. those that interact with other systems: network, db, etc) and I don't want to change the design just to accommodate test double DOC injection. How do you approach testing in such a scenario?
My recommendation, based on personal experience, is to write integration tests where DOCs (Depended On Components) are not mocked.
However, if for whatever reason the teams insists on having unit tests instead, you would have to either use a suitable mocking tool (AOP tools are able, but not a good fit here), or change the design of SUT and DOCs in order to use "weaker" mocking tools.

How (and whether) to modify the object in a Chai BDD chain

I am using ChaiJS with my Casper-Chai plugin, and I am not sure how to go about a particular issue I am encountering.
I would like to be able to write tests such as:
expect(casper).selector("#waldo").to.be.visible;
This is pretty straightforward, with a call such as
utils.addChainableMethod(chai.Assertion.prototype, 'selector',
selectorMethod, selectorChainableMethod);
utils.addMethod(chai.Assertion.prototype, 'visible', visibleMethod);
Where the *Method references are to functions that perform respective tests or chainable calls.
My question is what is the best way to have e.g. 'selector' modify descendants in the chain. Two options come to mind:
use utils.flag(chai, 'object') to change it to the selector; or
create a new flag with e.g. utils.flag(chai, 'casper-selector')
When 'visible' is called, it can read the respective flag. Where modifying the 'object' seems to be useful is when calling e.g. 'length' later-on. However, I am concerned somewhat about unexpected side-effects of changing the 'object'.
I may also want to modify the object, for 'length' tests, down the chain, such as:
// there can be only one!
expect(casper).selector("#waldo").length(1)
// but that one has 4 classes
expect(casper).selector("#waldo").class.to.have.length(4)
Grateful for any thoughts and input.
---- EDIT ----
Okay, so here is the conceptual challenge that gave root to Casper-Chai, that requires a bit of a description of what Casper is and why Casper-Chai should be a Chai plugin and not just an alternative to the existing Casper API. Casper is a wrapper around the PhantomJS headless web browser, and as such Casper runs two effectively two distinct virtual machines: A
"Controller" and a headless web browser.
There is no DOM or "document" or "window" object in the controller; the Controller is in this respect a lot like Node.js, albeit using the WebKit javascript parser. In parallel, PhantomJS starts a headless web browser. The Controller can then communicate through a PhantomJS/Casper API to the headless browser. The Controller can tell the headless browser what pages to load, what javascript to run (which is like typing javascript into the console), and even mimic events such as keyboard input and mouse clicks. The headless browser has a full DOM and javascript stack: it is a web-page loaded in WebKit. You can capture screenshots of what WebKit would render.
Casper-Chai is run in the Controller. Tests created in Mocha + Chai in the Controller are meant to evaluate against the state of the headless browser. Although we can copy state from the browser to the Controller and run tests on that copied state, my limited experimentation with that design revealed problems inherent to the design (i.e. efficiency, race conditions, performance, and potential side-effects). The problem is that the browser state is dynamic, complex, and can be unwieldy.
So taking John's example, expect(casper.find("#waldo")).to.be.visible would not work, since there is no DOM, unless the object returned by Casper did some sort of lazy evaluation/mediation. Even if we serialized and copied the HTML element there there is no CSS in the Controller. Then, even if there were CSS for #waldo, there is no way to test the hierarchy as if it were a web-browser. We would have to copy a substantial portion of the DOM and all the CSS and then replicate a web-browser to test whether #waldo is visible - for every single test. Casper-Chai is meant to avoid this problem by running the tests in the browser.
Just for a little extra illumination, a trivial comparison is getting the number of elements that match a selector. One could write expect(casper.evaluate(function () {return __utils__.findAll('.my_class')}).to.have.length(4), where casper.evaluate runs the given function in the headless browser and returns a list of DOM elements matching the selector as strings; and you can think of __utils__ as Casper's version of jQuery. Alternatively, one could write expect(casper).selector('.my_class').to.have.length(4) where selector becomes the 'object' and it has a getter .length that calls 'casper.evaluate(function () { return utils.findAll('.my_class').length`. Only the integer length is returned. For a short number of tests either works fine, but for larger numbers of tests this performance characteristic becomes impactful (here, in this simplistic form, and potentially to a substantially greater extent in more complex cases).
One could of course write expect(casper.evaluate(function () { __utils__.findAll('.my_class').length }).equal(4), but if one is going to write tests like this, why bother with BDD/Chai? It eliminates the benefit of readability that Chai offers.
It is also worth noting that there may be multiple Casper instances in the Controller, corresponding to multiple PhantomJS pages. Think of them as spooky tabs.
So given Domenic's answer that modifying the 'object' flag is the appropriate way to go about it, this seems the most practical way - subject to any thoughts in light of the above description.
I hope the above describes why Casper-Chai should be a plugin and not just an API extension to Casper. I'll also run this by Casper's author to see if he has any input.
It may not be a perfect relationship, but I am hopeful that Casper & Chai can get along handily. :)
The difficulty stems from the fact that casper has a highly procedural API, with methods like Casper#click(String selector) and Casper#fetchText(String selector). To fit naturally with chai, an object-oriented API is needed, e.g. Casper#find(String selector) (returning a CasperSelection object), CasperSelection#click(), CasperSelection#text(), etc.
Therefore I suggest you extend the casper object itself with a find or selector method which returns an object on which you can base your assertions. Then you will not need to change the object flag.
expect(casper.find("#waldo")).to.be.visible;
expect(casper.find("#waldo")).to.have.length(1)
expect(casper.find("#waldo").class).to.have.length(4)
I mostly agree with #John. You're performing your expectations on some other object, so saying
expect(casper).select("#waldo").to.have.length(1)
is very strange. You're not expecting anything about casper, you're expecting something about casper.find("#waldo"). Consider also the should syntax:
casper.should.select("#waldo").have.length(1)
// vs.
casper.find("#waldo").should.have.length(1)
That said, if you're dead set on this kind of API, this is exactly what the object flag is for. Chai even does this, to make assertions like
myObj.should.have.property("foo").that.equals("bar")
work well:
https://github.com/chaijs/chai/blob/49a465517331308695c3d8262cdad42c3ac591ef/lib/chai/core/assertions.js#L773