How can I combine Jest matchers, especially for mocked arguments? - jestjs

I have some tests that accurately test my code's expected behavior, but that are rather brittle because I'm using literals. I'd like to make broader assertions about the shape of something. For example:
expect(mockedModule.mockFunction).toHaveBeenCalledTimes(1);
expect(mockedModule.mockFunction).toHaveLastBeenCalledWith(
"argument1",
{
field1: expect.stringMatching(/^argument1/), // but I also care about several other string matches
// and I _do not_ care about the order.
},
);
I could obviously repeatedly call toHaveLastBeenCalledWith with expect.any() littered through my code. But that's not very succinct, and it's not a great way to express the invariants I'm expecting.
Ideally I'd have something like:
expect(mockedModule.mockFunction).toHaveLastBeenCalledWith(
"argument1",
{
field1: expect.all(
expect.stringMatching(/^argument1/),
expect.any(
expect.stringMatching("42"),
expect.stringMatching("8675309"),
)
)
},
)
or the equivalent:
expect(mockedModule.mockFunction).toHaveLastBeenCalledWith(
"argument1",
{
field1: expect.all(
expect.stringMatching(/^argument1/).and(
expect.stringMatching("42").or(
expect.stringMatching("8675309)
)
),
)
},
)
But I have not found any functionality similar to this. This is most important for toHaveBeenCalledWith calls, where I never get a copy of the actual value, but have to create a matcher.

Related

How can I best pattern match in Result::map

I know I can pattern match like this in rust
some_result.map(|some_number| {
match some_number {
1 => HttpResponse::NoContent().finish(),
_ => HttpResponse::NotFound().finish(),
}
})
but in Scala I can do like this
some_option.map {
case 1 => ???
case _ => ???
}
Is there a way to avoid the repetition of the variable some_number in the rust code above?
EDIT:
I found out i could do it this way, but i still think the original question answered my question best.
Ok(match result {
Ok(1) => HttpResponse::NoContent(),
Ok(_) => HttpResponse::NotFound(),
Err(_) => HttpResponse::InternalServerError()
}.finish())
its all about the context and in this case i didnt include much of it ...
EDIT #2:
Changed to another answer. I really like inverting the problem. And if else is not idiomatic rust afaik.
If we're just bike-shedding style, you could avoid introducing some_number entirely by matching on the whole result:
match some_result {
Ok(1) => Ok(HttpResponse::NoContent().finish()),
Ok(_) => Ok(HttpResponse::NotFound().finish()),
Err(e) => Err(e)
};
But this just trades some_number for some Oks and Errs. I would generally prefer the original style, but beauty is in the eye of the beholder.
There is no way that I know of to avoid the repetition, however I think it might be more idiomatic to simply write
some_result.map(
|some_number|
if some_number == 1 {
HttpResponse::NoContent().finish()
} else {
HttpResponse::NotFound().finish()
}
)
since there is no need for a match in such a simple situation.
EDIT: Why is an if statement more idiomatic than a match on in this situation?
The general idea is that match is more powerful than if (every if statement could be replaced by a match statement), therefore if is more specific, and thus should be used when possible (without matches!). The only exception is the switch/case use-case, which could be expressed as an if statement but a match one should be used.
But this is more of a guideline than an argument, so let's break down the reason why if is more idiomatic.
You start with something like
match some_number {
1 => { ... }
_ => { ... }
}
In the situation of
match x {
Pattern => { ... }
_ => { ... }
}
if let is more idiomatic. Since we're in this situation, we can rewrite
if let 1 = some_number { ... } else { ... }
However, in our case, we are matching a single literal, so it is more idiomatic to simply transform the if let into
if some_number == 1 { ... } else { ... }
The only exception is when you are planning to add more branching to the match statement, like
match some_number {
1 => { ... }
2 => { ... }
_ => { ... }
}
in which case it would make sense to keep it like that.
Keep in mind that being idiomatic also means being able to convey by the way you code your intention so that your programming becomes clear.
Note: Why is this more idiomatic than than simply matching the whole result?
Most of the time, being idiomatic is a synonym of being concise. If you are being verbose, it's a good hint you're not being idiomatic. However, it'is not always true, and this is a good example of being idiomatic meaning being more verbose.
When you are matching a result, you are expressing that you want to handle both the error and the ok case. When you are mapping, you are instead expressing that you are only interested in the ok case.
Most of the time, people don't want to handle manually the error case, and just add a ?. However, when they don't, most of the time they want to handle the error case. Finally, they might want not to handle the error, but also not to get rid of it right away.
These three choices are increasingly verbose to implement due to the frequency of usage. This means that you should not aim for the one that is less verbose, but instead for the solution that matches your intention, so that when one reads your code, it's easier to grasp your intention just by your choice structure of implementation.
In your original question, you seemed not to care about the error case, and also you didn't seem to want to get rid of it with ?, which is why I think that having an if statement inside a map is more idiomatic, in the sense that it is more clear and communicates better what you want to achieve. Indeed, I didn't even think about the error case, which is, IMO, what idiomatic means (ie. the capacity of adapting the way one thinks to ease the comprehension of code by writing it in the most expressive way, for a given language).
Finally, I would point out the most idiomatic choice for handling an error, that you didn't seem to take into account, and I wonder why.
if some_result? == 1 {
HttpResponse::NoContent().finish()
} else {
HttpResponse::NotFound().finish()
}
Where you have implemented an appropriate conversion from the eventual error type to the return type.

Jest equivalent of tape descriptions

I'm a long time tape.js user and I'm working on learning how to work jest. I'm interested in providing descriptions for each my test cases as part of the assertion, ala this tape test
function myCoolTest(t) {
t.equal('batman'.length, 6, 'batman should have the right number of characters in it');
t.ok(1 === 1, 'basic truths should stay true');
t.deepEqual({test: 1}, {test: 1}, 'deep equality of objects works sensibly');
t.end();
}
I like being able to annotate my tests (eg 'batman should have the right number of characters in it'), that way as I'm reading the output it's clear whats passed and what's failed. As far as I can tell the jest equivalent is
test('example test', () => {
expect('batman'.length).toBe(6);
expect(1 === 1).toBeTruthy();
expect({test: 1}).toBe({test: 1});
});
Which totally lacks description found in the first? While that's okay for simple examples like ^. The examples i've seen other places seem to suggest that if I want description I should add comments next to the relevant test, but this seems to prevent creating utility tests, eg
const expectEqual = (a: string, b: string): void =>
expect(JSON.parse(a)).toEqual(JSON.parse(b));
Am i just out of luck or are there methods that I am missing?

Is there a way to convert a graphql query string into a GraphQLResolveInfo object?

I have written a piece of software that parses and formats the fourth parameter of a graphql resolver function (the info object) to be used elsewhere. I would like to write unit tests for this software. Specifically, I do not want to build the GraphQLResolveInfo object myself, because doing that would be very cumbersome, error-prone and hard to maintain. Instead, I want to write human-readable query strings and convert them to GraphQLResolveInfo objects so I can pass those to my software.
After extensive googling and reading of the graphql-js source code, I have not found a simple way to do what they are doing internally. I'm really hoping that I am missing something.
What I am not trying to do is use the graphql-tag library, because that just generates an AST which has a very different format from the GraphQLResolveInfo type.
Has anyone done this before? Help would be much appreciated!
I will keep monitoring this question to see if a better answer comes along, but I've finally managed to solve my particular issue by creating as close an approximation of the GraphQLResolveInfo object as I need for my particular use case.
The GraphQLResolveInfo object is composed of several attributes, two of which are called fieldNodes and fragments. Both are in fact parts of the same AST that graphql-tag generates from a query string. These are the only parts of the GraphQLResolveInfo object that concern the software I wrote, the rest of it is ignored.
So here is what I did:
import gql from 'graphql-tag';
// The converter function
const convertQueryToResolveInfo = (query) => {
const operation = query.definitions
.find(({ kind }) => kind === 'OperationDefinition');
const fragments = query.definitions
.filter(({ kind }) => kind === 'FragmentDefinition')
.reduce((result, current) => ({
...result,
[current.name.value]: current,
}), {});
return {
fieldNodes: operation.selectionSet.selections,
fragments,
};
};
// An example call
const query = gql`
query {
foo {
bar
}
}
`;
const info = convertQueryToResolveInfo(query);
From the AST generated by graphql-tag, I extract and modify the operation and fragment definitions so that they look the way they do within the GraphQLResolveInfo object. This is by no means perfect and may be subject to change in the future depending on how my software evolves, but it is a relatively brief solution for my particular problem.

Groovy: unwrapping named map arguments in method

I'm finding myself passing a lot of variables to my methods as named arguments as it makes things a lot clearer:
doFunction(name: 'Jack', age: 27)
now in doFunction I often find myself doing this:
doFunction(Map args) {
if (args['name']) {
def name = args['name']
// do stuff with name
}
}
Is there a language feature to unwrap the Map to its respective parameters on the fly? I couldn't find anything like that. and if there wasn't, I'm curious as to why, it seems like this is the natural Groovy approach to boilerplate.
I'd like a way to immediately check and work on the parameter if it exists, is there a cleaner way to do this? am I approaching Map arguments the wrong way entirely?
Since there is no destructuring like e.g. in clojure, one way to work with maps like this would be using with. Like:
void destruct(Map params) {
params.with{
if (name) {
println "Hello $name"
}
if (age) {
println "I am $age years old"
}
}
}
destruct name: "World", age: 4.54e9
// => Hello World
// => I am 4.54E+9 years old
destruct name: "Computer"
// => Hello Computer
Also on the nitpicking side: those are no named arguments (like e.g. in python). It is just a syntactic sugar for passing maps. E.g. it is short for destruct([name: 'World']) -- it will not work for a method void destruct(String name, BigDecimal age)

Query value gets quoted automatically before sending it to MongoDB?

The following is confusing me a lot. I have been spending quite a bit of time trying to understand why collection.find() doesn't work with regex passed as an object. The regex match is coming over HTTP wrapped in the body of a POST request. Then I try to gather the query (in string format) and perform the query. The problem seems to be that unless the regex is written inside Node without quotes, it won't work. That is, it must be a literal without quotes.
For example, the following works fine:
var query1 = {
company: {
'$regex': /goog/
}
};
collection.find(query1, {}).toArray(function (err, docs) {
// Got results back. Awesome.
});
However, if the data comes wrapped in an object, it doesn't return anything. I suspect it's because the value gets quoted behind the scenes (i.e. "/goog/"):
// Assume
var query2 = {
company: {
'$regex': query.company
}
};
collection.find(query2, {}).toArray(function (err, docs) {
// Got nothing back.
});
I have tested it with the mongo shell and I can confirm the following:
// Returns 5 results
db.getCollection("contacts").find( { "company": /goog/ } )
// Doesn't match anything
db.getCollection("contacts").find( { "company": "/goog/" } )
Furthermore, I just discovered the following: if I write the value with quotes
// Works fine
var companyRegex = {'$regex': /goog/};
var query3 = {
company: companyRegex
};
So technically, a "literal" regex without quotes wrapped in an object works fine. But if it's a string, it won't work. Even after trying to replace the double-quotes and single-quotes with nothing (i.e. essentially removing them.)
Any idea how can I get the regex match be passed verbatim to find()? I've researched it, finding lots of potential solutions, alas it's not working for me.
Thanks in advance!
Let me focus on one line of your post. This is where the problem might be:
The regex match is coming over HTTP wrapped in the body of a POST request.
This seems problematic because:
The only structures that survive serialization between client/server are:
boolean
number
string
null *
objects and arrays containing these basic types
objects and arrays containing object and arrays [of more obj/array] of these basic types
Regexp, Date, Function, and a host of others require reconstruction, which means
passing a string or pair of strings for the match and option components of the Regexp and running Regexp() on the receiving end to reconstruct.
Regexp gets a bit messy because Regexp.toString() and Regexp() do not appear to be inverses of each others: /someMatch/.toString() is "/someMatch/" but RegExp("/someMatch/") is //someMatch// and what was needed instead to rebuild the regexp was just RegExp("someMatch"), which is /someMatch/. I hope this helps.
JSON.stringify(/someMatch/) is {} (at least on Chrome).
So instead of trying to build a general transport, I recommend re instantiating a particular field as a regexp.
* Irrelevant note: (null is fine but undefined is peculiar. JSON won't stringify undefineds in objects and turns undefined into null in Arrays. I recognize this isn't part of your problem, just trying to be complete in describing what can be serialized.)

Resources