npryce / hamkrest Goto Github PK
View Code? Open in Web Editor NEWHamcrest for Kotlin
License: Apache License 2.0
Hamcrest for Kotlin
License: Apache License 2.0
All the examples that use the assert.that()
style don't compile. The error is:
Function invocation 'assert(...)' expected
For example:
@Test fun `collection type inference`() {
assertThat(listOf(1) as Collection<Int>, equalTo(listOf(1)))
}
doesn't compile with error:
Error: Kotlin: None of the following functions can be called with the arguments supplied:
public fun <T> assertThat(actual: ???, criteria: Matcher<???>): Unit defined in com.natpryce.hamkrest.assertion
public fun <T> assertThat(actual: Collection<Int>, criteria: KFunction1<Collection<Int>, Boolean>): Unit defined in com.natpryce.hamkrest.assertion
If I remove assertThat
overloaded with KFunction1
compilation fails with:
Error: Kotlin: Type inference failed: Cannot infer type parameter T in fun <T> assertThat(actual: T, criteria: Matcher<T>): Unit
None of the following substitutions
(Collection<Int>,Matcher<Collection<Int>>)
(List<Int>?,Matcher<List<Int>?>)
can be applied to
(Collection<Int>,Matcher<List<Int>?>)
Similar code works in Java version:
@Test public void collection_type_inference() {
assertThat(((Collection<Integer>) asList(1)), equalTo(asList(1)));
}
Probably because it's a Kotlin function as well. It's just a minor annoyance that I've been having. Does anyone have workarounds or alternatives that I don't know about? Using assertThat
instead of assert.that
works fine.
I'm looking to use some newer features in HamKrest, like allOf
. These were added since the last tagged/released 1.4.2.2 version. Is there any ETA when these will be published to Maven Central as a new version?
Rik
So you can say
value shouldEqual 42
instead of
value shouldMatch equalTo(42)
Or perhaps it is just another version of shouldMatch and you say
value shouldMatch 42
Guava has a bunch of it's own collection types. It would be really cool if there was a hamkrest-guava
library that added matchers for these types.
Some of the matchers that I've written up for my own use case are these:
fun <K, V> hasKey(element: K): Matcher<Multimap<K, V>> = object : Matcher.Primitive<Multimap<K, V>>() {
override fun invoke(actual: Multimap<K, V>): MatchResult =
if (actual.containsKey(element)) {
MatchResult.Match
} else {
MatchResult.Mismatch("was ${describe(actual)}")
}
override val description: String get() = "contains key ${describe(element)}"
override val negatedDescription: String get() = "does not contain key ${describe(element)}"
}
fun <K, V> hasValue(element: V): Matcher<Multimap<K, V>> = object : Matcher.Primitive<Multimap<K, V>>() {
override fun invoke(actual: Multimap<K, V>): MatchResult =
if (actual.containsValue(element)) {
MatchResult.Match
} else {
MatchResult.Mismatch("was ${describe(actual)}")
}
override val description: String get() = "contains value ${describe(element)}"
override val negatedDescription: String get() = "does not contain value ${describe(element)}"
}
fun <K, V> contains(key: K, value: V): Matcher<Multimap<K, V>> = object : Matcher.Primitive<Multimap<K, V>>() {
override fun invoke(actual: Multimap<K, V>): MatchResult =
if (actual.containsEntry(key, value)) {
MatchResult.Match
} else {
MatchResult.Mismatch("was ${describe(actual)}")
}
override val description: String
get() =
"contains key ${describe(key)} with value ${describe(value)}"
override val negatedDescription: String
get() =
"does not contain key ${describe(key)} with value ${describe(value)}"
}
Sometimes it is useful not to evaluate the reason expression until the assertion failed. Example:
val userJson = JsonPath.parse(userResponseBody)
assertThat(
userJson.read("$.error_description"),
userResponse.status,
equalTo(Response.Status.OK.statusCode)
)
Possible solution would be
fun <T> assertThat(actual: T, matcher: Matcher<in T>, reason: () -> String) {
if (!matcher.matches(actual)) {
val description = StringDescription()
description.appendText(reason.invoke())
.appendText("\nExpected: ")
.appendDescriptionOf(matcher)
.appendText("\n but: ")
matcher.describeMismatch(actual, description)
throw AssertionError(description.toString())
}
}
fun <T> assertThat(reason: String, actual: T, matcher: Matcher<in T>) =
assertThat(actual, matcher) { reason }
Is it possible to add hamkrest as test dependency in multiplatform package?
I am trying to do this
commonTest { dependencies { implementation kotlin('test-common') implementation kotlin('test-annotations-common') implementation 'com.natpryce:hamkrest:1.7.0.0' } }
but that doesn't work. I can see the lib in Intellij but classes are not accessible in tests
If I am missing something, what is the idiomatic way to do it HamKrest?
Otherwise, are there any plans to implement this (are pull requests welcome)?
Right now there're tons of libs relying on Hamcrest matchers, such as rest-assured, which is also has Kotlin support.
When I saw your library first time I was pretty surprised. It was until I realised that Hamkrest is not compatible with Hamcrest interfaces. That's really disappointing, as all of your syntactic sugar renders useless for me. The only question is why?
Maybe is it possible to make your awesome lib compatible with Hamcrest?
If not, don't you know any alternatives?
I was pretty surprised that the anything matcher doesn't allow things like this:
val value: Int? = null
value shouldMatch anything
Is there any reason for the anything
Matcher to be of type Matcher<Any
and not Matcher<Any?>
?
If not I would be happy to work on a (very small) pull request to fix this issue.
If there is a reason I'm interested in hearing it.
What about adding more syntactic sugar to have assertions that reads even better like :
"banana" should starts with "bana"
Kotlintest has something similar so I know it's doable :)
I'll be happy to PR this if interested.
Kotlin 1.1 has moved reflection classes into a new package, and deprecated the ones in the kotlin.reflect package
Since this is for Kotlin and we have infix functions seems natural that instead of saying something like:
assertThat(myValue, eq(5))
that it would be much nicer to have infix versions (which I am renaming to assertThatIt for a more natural wording):
myValue assertThatIt eq(5)
It would be nice to have comparison matchers that could be used like this:
@Test
fun comparisonExample() {
val duration = getDurationFromSomewhare()
assertThat(duration, isLessThan(Duration.ofSeconds(2)))
}
This might be outside of the scope of this assertion library but a kotlin friendly way of declaring multiple assertions in a single test would be nice to add.
I'm opening this issue due to the discussion in the Junit5:
Is this outside the scope of this project? If not, this method should probably throw the org.opentest4j.MultipleFailuresError
which would require creating a dependency on opentest4j
Hi, I'm trying to install by adding to my app's build.gradle:
testCompile 'com.natpryce.hamkrest:1.4.2.0'
but wasn't in your README, and it fails to resolve, and I found that this plugin is missing on plugins.gradle.org. I haven't authored a plugin myself but I believe this just requires publishing to plugins.gradle.org. That would make this library infinitely more useful. Thanks!
The following test does not compile but should:
val message: String? = null
assertThat(message, isNullOrBlank)
your version unfortunately is not compatible with the latest Kotlin version: 1.0.0-rc-1036. Could you please update it?
I struggled to write an assertion without creating custom matchers to assert on a nullable boolean, this is what i ended up with but
I don't think it reads very well;
assertThat(capturedValue, false::equals)
Do you have any advice?
Hi,
I stumbled across Hamkrest only recently, as I'd been incorrectly assuming that these were the only implementations of Hamcrest matchers. And I also see that Hamkrest has existed now for almost 3 years, which would seem to suggest that there are no plans to move it under the Hamcrest umbrella either.
Can you tell me what Hamkrest's roadmap is please? I can't help but think of the Yammer/CodaHale/DropWizard metrics library here and wonder if the Hamkrest artifacts could ever mutate in such a way.
Thanks,
Chris
On Windows 10, the test DelimitingValuesInStrings::file_paths
fails (and I assume it does not fail on non-Windows platforms). The error is:
java.lang.AssertionError: expected: a value that is equal to "/foo/bar/baz"
but was: "\\foo\\bar\\baz"
Expected :a value that is equal to "/foo/bar/baz"
Actual :"\\foo\\bar\\baz"
It looks like this is a Windows-specific issue given that the failure occurs because the platform's separator is different.
http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html#containsInAnyOrder(T...)
assertThat(listOf("foo", "bar"), containsInAnyOrder("bar", "foo"))
In IntelliJ, opening the 'External Libraries' view and finding Hamkrest, some of the 'files' are listed with a .class extension (CollectionMatchers.class) and some are not (CaseSensitity). The source for the latter is shown, the former just shows a decompiled version.
It applies Maven and Gradle projects imported into IntelliJ, and certainly hampers my ability to work out how to use the library.
While doing some checks on the same item that requires checking multiple things it occurred to me that this function:
fun <T> assertThat(actual: T, criteria: Matcher<T>) {
_assertThat(null, actual, criteria)
}
could be more convenient as:
fun <T> assertThat(actual: T, vararg criteria: Matcher<T>) {
criteria.foreach {_assertThat(null, actual, it) }
}
I appreciate that with Kotlin you could make the API much nicer, but the fact that your Matcher does not implement Hamcrest's Matcher interface is a very bad idea. Hamcrest is pretty much a standard, it is supported by JUnit itself and other libraries like Mockito. Hamkrest should augment Hamcrest not be a incompatible replacement for it.
Trying to do my due diligence and document and credit all libraries I am using including noting the license, but I notice that you haven't actually declared a license in GitHub
When running tests in parallel, we are seeing threading problems with the following line of code:
private val descriptionServices = ServiceLoader.load(ValueDescription::class.java)
https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html says “Instances of this class are not safe for use by multiple concurrent threads.”
E.g. the following assertion currently fails:
assertThat(sequenceOf(1, 2, 3), equalTo(sequenceOf(1, 2, 3)))
val isNullOrBlank = Matcher(CharSequence::isNullOrBlank)
val isNullOrEmptyString = Matcher(CharSequence::isNullOrEmpty)
should be:
val isNullOrBlank = Matcher(CharSequence?::isNullOrBlank)
val isNullOrEmptyString = Matcher(CharSequence?::isNullOrEmpty)
Hello,
Love using hamkrest! but I have one gripe with it. IntelliJ doesn't show the "show difference" option when an assertion fails. This is quite handy when you're asserting the contents of data classes.
A quick google shows that IntelliJ offers this functionality simply by checking a regular expression. Would it be possible to change the description strings so that IntelliJ shows the "show difference" option when using hamkrest?
Kind regards,
Ewout.
Something that would just make writing test simpler:
/**
* Matches a collection with a size that is equal to [size].
*/
fun hasSize(size: Int) = has(Collection<Any>::size, equalTo(size))
If you have an assertion like this:
assertThat(somePath, equalTo(fileSystem.getPath("some.file")))
and the assertion fails (ie. somePath != fileSystem.getPath("some.file")
), instead of failing with an assertion failed message, a StackOverflowError is thrown.
The stack trace is:
java.lang.StackOverflowError
at sun.nio.fs.UnixPath.initOffsets(UnixPath.java:206)
at sun.nio.fs.UnixPath.getName(UnixPath.java:319)
at sun.nio.fs.UnixPath.getName(UnixPath.java:43)
at sun.nio.fs.AbstractPath$1.next(AbstractPath.java:80)
at sun.nio.fs.AbstractPath$1.next(AbstractPath.java:71)
at com.natpryce.hamkrest.Describe.describe(describe.kt:43)
at com.natpryce.hamkrest.Describe.describe(describe.kt:19)
at com.natpryce.hamkrest.Describe.describe(describe.kt:19)
at com.natpryce.hamkrest.Describe.describe(describe.kt:19)
...
It looks like the issue is that Path
implements Iterable<Path>
, and each of those returned Path
s are themselves Iterable<Path>
s, and so describe
just keeps recursing. Path
probably shouldn't be treated as an Iterable
anyway, given that the standard string representation would be much easier to understand.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.