5 ms·
Here's a test I wrote recently. The code in question is part of a nodejs service that talks to a database and produces json objects to be used by a web client a
by winstonewert 10y ago
Here's a test I wrote recently. The code in question is part of a nodejs service that talks to a database and produces json objects to be used by a web client application.
There is a definition for the schema to a fruit table.
Fruit: {
list_id: 32
}
The list_id indicates that this isn't a real table, but is actually stored in the list_options table. So my code has to generate a different query then for a normal table.
The actual options are populated from a list:
const LIST_OPTIONS = [
[32, "Blueberry", 1],
[32, "Apple", 2],
[32, "Orange", 45],
[45, "Nope", 1]
]
These get inserted into the database. Note that the first column is the list_id from the schema.
And for the actual test:
This actual sends the request to be processed.
let response = await processRequest({
type: 'QUERY',
table: 'Fruit',
})
Then I assert that the respone is exactly what I thought it would be:
deepStrictEqual(response, {
status: 'OK',
records: [{
id: "Blueberry",
name: "Blueberry",
position: 1
}, {
id: "Apple",
name: "Apple",
position: 2
}, {
id: "Orange",
name: "Orange",
position: 45
}]
I would have written pretty much the exact same test if it were a statically typed language.
- lmm 10y agoOk, good example on some levels, though it's doing a lot of interfacing with untyped parts. In a real-world scenario I would be looking to extend the type safety by using thrift or similar rather than json, and ideally a strongly typed datastore (for a static list like this, probably plain old code rather than a database). That aside, what pieces could actually go wrong in a statically typed language? You can't typo field names or the like. You could typo the names, but I don't believe typing them into the computer twice adds value - better to check them once in code review. People aren't going to be editing this code in a way that could lead to getting the names wrong. You couldn't go down the wrong code path and end up with the "normal table" query, because having list_id instead of something else is part of the type. You couldn't pass some other number as the list id, because list ID is a typed thing that is different from all other IDs, and in any case where would an outside number come from? And you're definitely going to return a response that's formatted with a record list, because that's part of the type of the method. (You might need to test the JSON <-> String piece, but that's generic, so you only need an O(1) test for that, you don't have to test every JSON endpoint. In practice you're likely using an existing well-tested library. Similar reasoning applies to the database querying part). Assuming you have warn-for-unused-parameters enabled, you couldn't forget to apply the criterion and just return everything in the list. So yeah, assuming this was for part of an existing system where I'd already tested generic facilities like database access and JSON serialization, I wouldn't feel the need to write this test at all.
- winstonewert 10y ago> So yeah, assuming this was for part of an existing system where I'd already tested generic facilities like database access and JSON serialization, I wouldn't feel the need to write this test at all. But this is the generic facility for database access. This is just an additional special case it has to handle. Does that change your perspective on whether you would write a test for it? > That aside, what pieces could actually go wrong in a statically typed language? Well, I could type the SQL wrong, but let's assume we are working in a sufficiently awesome statically typed language that would catch that kind of error. I could filter on the wrong column, with the wrong operator, with the wrong value, on the wrong table. I could decode the name column using the wrong encoding. I could return multiple rows per option. I could do a huge number of other things that I couldn't think of. These may not be really likely to be mistakes I'd make (this is a pretty simple task), but several refactorings down the line, I can easily see an issue being introduced. At the end of the day, even in a statically typed language where much of the functionality would be verified by the compiler, I'd still like to have a test on this. > Ok, good example on some levels, though it's doing a lot of interfacing with untyped parts Hmm... the fact that it deals with the boundary of the system and the fact that it deals only with converting data from one format to another might render it a poor sample (i.e. there's not really much interesting logic). I can come up with another if you are interested.
- lmm 10y ago> But this is the generic facility for database access. This is just an additional special case it has to handle. Presumably it would be using a lower layer - or more likely an existing library - for doing the actual query though? > I could filter on the wrong column, Not if you've got a typed representation of which column's which > with the wrong operator, Not if equality is the only operator available. This is one of my biggest reasons for using wrapper types around IDs - you shouldn't treat IDs as integers, because the idea of adding or multiplying or <=ing an ID makes no sense. > with the wrong value, How? You have to use the passed ID for something, and it can't do anything else. > on the wrong table. So make the table part of the type. A list ID is a different thing from a user ID, they shouldn't be compatible. > I could decode the name column using the wrong encoding. Encoding should absolutely be handled with types. > I could return multiple rows per option. Hmm, maybe. But I can't see how you'd do that by accident. > At the end of the day, even in a statically typed language where much of the functionality would be verified by the compiler, I'd still like to have a test on this. I started out thinking the same thing (I moved from Java/Python to Scala). I've gradually reduced the amount of tests I write over four years as I've found them to not add value. I was as surprised as anyone, and even privately entertained the thought that maybe I was some kind of super-programmer who was smarter than all those people who needed tests, but that month or two of trying to do stuff in Django made it very clear that it wasn't me, it was the language. > Hmm... the fact that it deals with the boundary of the system and the fact that it deals only with converting data from one format to another might render it a poor sample (i.e. there's not really much interesting logic). I can come up with another if you are interested. Yeah, happy to if you're interested. It's a useful exercise in thinking it through for myself as much as anything.