30-seconds-of-code: Test ~ Log

Created on 10 Jan 2018  路  23Comments  路  Source: 30-seconds/30-seconds-of-code

Similarly to the #100, we can keep track of snippets that are missing test cases.

Here is the list of snippets that need their test cases implemented:

Be sure to tick the ones that you or someone else solved. If you don't have access to edit this file be sure to let us know in the comments about the changes

  • [x] anagrams
  • [x] arrayToHtmlList
  • [x] ary
  • [x] atob
  • [x] attempt
  • [x] average
  • [x] averageBy
  • [x] bind
  • [x] bindAll
  • [x] bindKey
  • [x] bottomVisible
  • [x] btoa
  • [x] byteSize
  • [x] call
  • [x] capitalize
  • [x] capitalizeEveryWord
  • [x] castArray
  • [x] chainAsync
  • [x] chunk
  • [x] clampNumber
  • [x] cloneRegExp
  • [x] coalesce
  • [x] coalesceFactory
  • [x] collectInto
  • [x] colorize
  • [x] compact
  • [x] compose
  • [x] composeRight
  • [x] copyToClipboard
  • [x] countBy
  • [x] countOccurrences
  • [x] createElement
  • [x] createEventHub
  • [x] currentURL
  • [x] curry
  • [x] debounce
  • [x] decapitalize
  • [x] deepClone
  • [x] deepFlatten
  • [x] defaults
  • [x] defer
  • [x] delay
  • [x] detectDeviceType
  • [x] difference
  • [x] differenceBy
  • [x] differenceWith
  • [x] digitize
  • [x] distance
  • [x] drop
  • [x] dropRight
  • [x] dropRightWhile
  • [x] dropWhile
  • [x] elementIsVisibleInViewport
  • [x] elo
  • [x] equals
  • [x] escapeHTML
  • [x] escapeRegExp
  • [x] everyNth
  • [x] extendHex
  • [x] factorial
  • [x] fibonacci
  • [x] filterNonUnique
  • [x] findKey
  • [x] findLast
  • [x] findLastIndex
  • [x] findLastKey
  • [x] flatten
  • [x] flip
  • [x] forEachRight
  • [x] formatDuration
  • [x] forOwn
  • [x] forOwnRight
  • [x] fromCamelCase
  • [x] functionName
  • [x] functions
  • [x] gcd
  • [x] geometricProgression
  • [x] get
  • [x] getDaysDiffBetweenDates
  • [x] getScrollPosition
  • [x] getStyle
  • [x] getType
  • [x] getURLParameters
  • [x] groupBy
  • [x] hammingDistance
  • [x] hasClass
  • [x] hasFlags
  • [x] hashBrowser
  • [x] hashNode
  • [x] head
  • [x] hexToRGB
  • [x] hide
  • [x] httpGet
  • [x] httpPost
  • [x] httpsRedirect
  • [x] indexOfAll
  • [x] initial
  • [x] initialize2DArray
  • [x] initializeArrayWithRange
  • [x] initializeArrayWithRangeRight
  • [x] initializeArrayWithValues
  • [x] inRange
  • [x] intersection
  • [x] intersectionBy
  • [x] intersectionWith
  • [x] invertKeyValues
  • [x] is
  • [x] isAbsoluteURL
  • [x] isArrayLike
  • [x] isBoolean
  • [x] isDivisible
  • [x] isEmpty
  • [x] isEven
  • [x] isFunction
  • [x] isLowerCase
  • [x] isNil
  • [x] isNull
  • [x] isNumber
  • [x] isObject
  • [x] isObjectLike
  • [x] isPlainObject
  • [x] isPrime
  • [x] isPrimitive
  • [x] isPromiseLike
  • [x] isSorted
  • [x] isString
  • [x] isSymbol
  • [x] isTravisCI
  • [x] isUndefined
  • [x] isUpperCase
  • [x] isValidJSON
  • [x] join
  • [x] JSONToFile
  • [x] last
  • [x] lcm
  • [x] longestItem
  • [x] lowercaseKeys
  • [x] luhnCheck
  • [x] mapKeys
  • [x] mapObject
  • [x] mapValues
  • [x] mask
  • [x] matches
  • [x] matchesWith
  • [x] maxBy
  • [x] maxN
  • [x] median
  • [x] memoize
  • [x] merge
  • [x] minBy
  • [x] minN
  • [x] negate
  • [x] nthArg
  • [x] nthElement
  • [x] objectFromPairs
  • [x] objectToPairs
  • [x] observeMutations
  • [x] off
  • [x] omit
  • [x] omitBy
  • [x] on
  • [x] once
  • [x] onUserInputChange
  • [x] orderBy
  • [x] over
  • [x] overArgs
  • [x] palindrome
  • [x] parseCookie
  • [x] partial
  • [x] partialRight
  • [x] partition
  • [x] percentile
  • [x] pick
  • [x] pickBy
  • [x] pipeAsyncFunctions
  • [x] pipeFunctions
  • [x] pluralize
  • [x] powerset
  • [x] prettyBytes
  • [x] primes
  • [x] promisify
  • [x] pull
  • [x] pullAtIndex
  • [x] pullAtValue
  • [x] pullBy
  • [x] randomHexColorCode
  • [x] randomIntArrayInRange
  • [x] randomIntegerInRange
  • [x] randomNumberInRange
  • [x] readFileLines
  • [x] rearg
  • [x] redirect
  • [x] reducedFilter
  • [x] reduceSuccessive
  • [x] reduceWhich
  • [x] remove
  • [x] removeNonASCII
  • [x] reverseString
  • [x] RGBToHex
  • [x] round
  • [x] runAsync
  • [x] runPromisesInSeries
  • [x] sample
  • [x] sampleSize
  • [x] scrollToTop
  • [x] sdbm
  • [x] serializeCookie
  • [x] setStyle
  • [x] shallowClone
  • [x] show
  • [x] shuffle
  • [x] similarity
  • [x] size
  • [x] sleep
  • [x] sortCharactersInString
  • [x] sortedIndex
  • [x] sortedIndexBy
  • [x] sortedLastIndex
  • [x] sortedLastIndexBy
  • [x] splitLines
  • [x] spreadOver
  • [x] standardDeviation
  • [x] stripHTMLTags
  • [x] sum
  • [x] sumBy
  • [x] sumPower
  • [x] symmetricDifference
  • [x] symmetricDifferenceBy
  • [x] symmetricDifferenceWith
  • [x] tail
  • [x] take
  • [x] takeRight
  • [x] takeRightWhile
  • [x] takeWhile
  • [x] throttle
  • [x] times
  • [x] timeTaken
  • [x] toCamelCase
  • [x] toDecimalMark
  • [x] toggleClass
  • [x] toKebabCase
  • [x] tomorrow
  • [x] toOrdinalSuffix
  • [x] toSafeInteger
  • [x] toSnakeCase
  • [x] transform
  • [x] truncateString
  • [x] truthCheckCollection
  • [x] unary
  • [x] unescapeHTML
  • [x] unfold
  • [x] union
  • [x] unionBy
  • [x] unionWith
  • [x] uniqueElements
  • [x] untildify
  • [x] unzip
  • [x] unzipWith
  • [x] URLJoin
  • [x] UUIDGeneratorBrowser
  • [x] UUIDGeneratorNode
  • [x] validateNumber
  • [x] without
  • [x] words
  • [x] xProd
  • [x] yesNo
  • [x] zip
  • [x] zipObject
  • [x] zipWith

The list might be long but we already have nearly 400 test cases over 700 test cases. A lot of work has been done.

help wanted opinions needed test needed

Most helpful comment

I think this comment might show how much of an edge case can appear when you don't think one can:

head({ 0: true, length: 1 })

After all our API is just something with the key 0. Any numerically keyed entity has a "head"( 0 key or index ) in ES/JS

Now this isn't to say head is wrong. I wouldn't change it at all, but we did implement it to be compatible with any "head" object, and as such we should keep it that way( Arrays are after all Objects )

This does show we'll miss some edge cases so we'll want to go through and think about how the code works itself.

All 23 comments

Hey 馃憢 馃槃 @fejes713 Awesome Idea

Here's my suggestions.

Before being able to complete the list or check off a test as completed we 1st need to decide on the following

  • How many test on average we want each snippet to have
  • How our snippets are expected to be tested. i.e. type, timeout speed, expected result, Boolean, etc

@kingdavidmartins Agree with everything you said. 馃憤
As for the timeout speed, Promises are going to be a problem also right?

We should consider opinions from others too before we decide what's going to be our criteria for snippet tests.

20-25 test cases at most sounds reasonable. No more than we need to ensure the snippets work, so that we do not have to wait hours for tests to run.

12s timeout (inspired by Codewars) for non-promise snippets, no timeout for promise ones, but no running in the automated cron job, only on custom jobs or locally?

Tests on average is an awful idea. Instead code tests with comments on why the test is valid, and what edge case it is addressing.

If there should be no edge cases, it should instead just be a few tests. Really testing is meant for more than just making sure it works.

Tests on average is an awful idea.

@skatcat31 I agree & by the following statement

How many test on average we want each snippet to have

I meant more of a standard/min amount of test per snippet to ensure the functionality/quality and less of an actual average amount of test for all snippets

Although when reading said statement I can see why you could think that.

@Chalarangelo I believe the way to go would be coming up with a min amount of test per snippet & then retroactively increasing the amount of test per snippet respective to snippet complexity

As I am sure we can all agree that

const head = arr => arr[0];

Doesn't need 20 ~ 25 test

Generally speaking, most snippets require like 3-4 test cases at most, as they have very few edge-cases that need to be addressed. That being said, there are snippets that require a ton of tests for edge-cases and others that are based on promises etc, that might make testing on Travis a bit complicated due to timing issues (especially when Travis' build times are extremely inconsistent). Let's try and finish up as many simple snippets as possible first, such as head, which is a prime example of snippets that might need less than 5 test cases (long list, single element list, empty list, null or invalid input), and then move to the more complex ones where we need more test cases and more input about potential edge-cases.

@Chalarangelo I agree, Although head was a very simple example I believe a min of at least 8 ~ 10 test should suffice which is included but not limited to checking for

  • long list
  • single element list
  • empty list
  • null
  • invalid inputs
  • valid inputs
  • result with some omitted args
  • result with all args
  • result with no args | error
  • error
  • type

@Chalarangelo I will also start adding basic type testing to all the snippets, & so on retroactively

I think this comment might show how much of an edge case can appear when you don't think one can:

head({ 0: true, length: 1 })

After all our API is just something with the key 0. Any numerically keyed entity has a "head"( 0 key or index ) in ES/JS

Now this isn't to say head is wrong. I wouldn't change it at all, but we did implement it to be compatible with any "head" object, and as such we should keep it that way( Arrays are after all Objects )

This does show we'll miss some edge cases so we'll want to go through and think about how the code works itself.

@fejes713 I don't think you meant to close this

Yeah, I didn't. Thanks 馃憤

Hey @skatcat31 I def agree with the following

Now this isn't to say head is wrong. I wouldn't change it at all, but we did implement it to be compatible with any "head" object, and as such we should keep it that way( Arrays are after all Objects )

That being said I believe head({ 0: true, length: 1 }) is actually a valid input and shouldn't be considered an "edge case".

Similarly to

const head = (arr) => arr[0];
head('King') // 'K'

It should work with anything that can be accessed by index | key ~ 0

When testing the same input with lodash head() method it actually behaves the same. The only difference is that lodash head method handles null & undefined as an input by doing the following

const head = (arr) => (arr != null && arr.length) ? arr[0] : undefined;

instead of doing

const head = arr => arr[0];

which breaks and throws an error when passing null or undefined

Which I believe is a pattern that manifests itself when reviewing other libraries code. Ensuring code doesn't necessarily return an error but a undefined or [] or {} depending on the expected input makes things way more consistent and ensures that if something ever breaks when using our code our users can be sure it isn't because of our code not being able to handle said input but them not properly handling said output which also sounds like something I think our project can also benefit from

@kingdavidmartins we aren't a library. Library's have type checking or defined error handling. We decided against it so that each snippet is short, easy to understand, and self contained. We're more of a collection.

However undefined returns are perfectly acceptable in some contexts as long as it would still cause an error in most use cases so that the error bubbles in the proper context in code. The usual way to tell is the question "Is what I'm expecting an instance?", and if so then returning undefined is perfectly fine if the don't provide a proper instance, or throwing on especially bad ones where an undefined would be a travesty to return.

Head I will agree CAN be implemented with the implicit return because it's expecting an instance to de-reference a specific context from, however crashing is also perfectly acceptable since it is up to a developer to know what their code is doing at any one point in time.

however crashing is also perfectly acceptable since it is up to a developer to know what their code is doing at any one point in time.

@skatcat31 Yea, Makes sense! Honestly believe leaving head & all others as is the way to go.

we aren't a library. Library's have type checking or defined error handling. We decided against it so that each snippet is short, easy to understand, and self contained. We're more of a collection

I know I know 馃槶 馃挃 Although we are currently a collection of JS snippets. I sometimes get impatient since I can't wait for us to get started on a lib with all the bell's & whistle's that's also production ready.

...I'll just have to be patient 馃悓 until then

@kingdavidmartins agreed on the hurry and wait 馃槃 But at that point the biggest question becomes something entirely different

I just updated the log at the top, should (mostly) reflect all snippets that have been tested so far. Let's add tests for the rest!

If you stumble upon snippets that are difficult to test, kindly comment below and explain why/how they are problematic, so we can figure it out together!

Problematic snippets in terms of testing

  • Anything that produces random values: I propose we jsut test these manually once and just check them as tested in the log. It's almost impossible to evaluate them via unit tests.
  • Browser-specific snippets: We should override the normal snippet code and use jsdom to test them. Still, there are a few outliers that can only be tested manually.
  • Timing snippets: Some of these are hard to evaluate if they add the proper delay etc. I have no proposal for these, feel free to comment below.
  • Snippets that work on specific environments: Things like isTravisCI and colorize are impossible to test as far as I can tell. Manual testing should do the trick.

For manual tests, you should add t.pass('Tested by {your username} on {current date}') so that we know what has been tested manually. Possibly not the best option but we should be able to verify that everything works properly like that.

@Chalarangelo I think we can test the random snippets. For example for the randomeHexColorCode we can just check if it start with a # has a length of seven and contains nothing other than letter a-f and numbers. We can do the same for others can't we?

As far as I can tell, all snippets that are easily testable (i.e. do not fall in any of the above categories) have at least one or two tests specified for them, so most of our codebase is safe to use (about 81% test coverage right now).

I have been adding a few custom tests for some snippets, bypassing limitations such as the lack of the Blob class, not having a XMLHttpRequest implmentation in node and utilizing console.debug, so that we can ensure that snippets work.

I'm closing the issue as all snippets that are testable now have automatic tests. Some tests were performed manually, so if changes are made to those snippets, we will have to re-evaluate their tests by hand (but that's like 10% of the codebase). A huge thanks to everyone who has contributed!

The small step for us, the big one for JS learners.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for any follow-up tasks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Chalarangelo picture Chalarangelo  路  5Comments

ecwyne picture ecwyne  路  4Comments

GrosSacASac picture GrosSacASac  路  3Comments

fejes713 picture fejes713  路  4Comments

Lucien-X picture Lucien-X  路  5Comments