Add option to ignore differences in line endings during string comparison, so tests are not OS dependent
See original GitHub issueWhen writing tests that verify multi-line strings, for example file contents, it’s common to use Kotest string assertions. However these tests are highly sensitive to line endings.
This assertion will pass on Unix, and fail on Windows:
This PR also fixed line-ending tests failing https://github.com/kotest/kotest/pull/2743
I have some thoughts on how to implement this.
Additional arg on matchers
One option is to add an arg, like ignoreLineEndings
, to all string comparison matchers which defaults to true, but this would mean they could no longer be infix fun
s. Adding an overload with a ignoreLineEndings
arg could work around that, but that’s a lot of work, code, maintenance, and testing.
Example: new overloads are verbose and repetitive
// existing function infix fun <A : CharSequence?> A.shouldStartWith(prefix: CharSequence): A { this should startWith(prefix) return this } // new overload fun <A : CharSequence?> A.shouldStartWith(prefix: CharSequence, ignoreLineEndings: Boolean = false): A { this shouldNot startWith(prefix, ignoreLineEndings) return this } // existing function infix fun <A : CharSequence?> A.shouldNotStartWith(prefix: CharSequence): A { this shouldNot startWith(prefix) return this } // new overload fun <A : CharSequence?> A.shouldNotStartWith(prefix: CharSequence, ignoreLineEndings: Boolean = false): A { this shouldNot startWith(prefix, ignoreLineEndings) return this }
Boolean vs Sealed class flag
Even if there is an additional arg, there are drawbacks to using a boolean flag, as it is restricted in allowing for different options. Instead a sealed class/enum parameter would allow for more customisation, for example controlling exactly how the line endings are handled. Perhaps someone wants line endings in the ‘expected’ string to be converted from /r/n
to /n
, but on the ‘actual’ string left alone. Or vice versa, or both.
Project config option
A sealed class flag could be added to all string comparators, but it would be very verbose and repetitively. I think that all strings in a project would be compared the same way, there’s no need for a per-assertion config, and a flag only needs to be set once.
One propose adding a project configuration option to control how line endings are dealt with.
object KotestProjectConfig : AbstractProjectConfig() {
override val stringLineEndingsMode = StringLineEndings.MapToUnix
}
This flag can then be used in all string comparison matchers (but I’m not sure how this can be done… do the matchers have access to the project config?)
Helper functions
Possibly the easiest option is just providing some helper functions, although their use in tests could be pretty repetitive and verbose
// note: this is just a quick example, the regex isn't correct
private val windows = Regex("\r\n")
private val unix = Regex("\n")
fun String.lineEndingsToUnix() = replace(windows, "\n")
fun String.lineEndingsToWindows() = replace(unix, "\r\n")
Issue Analytics
- State:
- Created a year ago
- Comments:10 (8 by maintainers)
Top GitHub Comments
I like the last suggestion too as we could have multiple options like trim and case insensitive
I like it too. Perhaps the pattern could also be re-used for other things that also takes optional configuration, such as: