willowtreeapps / assertk Goto Github PK
View Code? Open in Web Editor NEWassertions for kotlin inspired by assertj
License: MIT License
assertions for kotlin inspired by assertj
License: MIT License
Tests have been rewritten several times now and they are all over the place. Need to consolidate them to one style. Updated tests have a class name ending in 'Test', where ones that need to be ported over end in 'Spec' (used to be using the Spek test framework). BooleanTest is a good exmaple of the desired format:
package test.assertk.assertions
import assertk.assert
import assertk.assertions.isFalse
import assertk.assertions.isTrue
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails
class BooleanTest {
//region isTrue
@Test fun isTrue_true_value_passes() {
assert(true).isTrue()
}
@Test fun isTrue_false_value_fails() {
val error = assertFails {
assert(false).isTrue()
}
assertEquals("expected to be true", error.message)
}
//endregion
//region isFalse
@Test fun isFalse_false_value_passes() {
assert(false).isFalse()
}
@Test fun isFalse_true_value_fails() {
val error = assertFails {
assert(true).isFalse()
}
assertEquals("expected to be false", error.message)
}
//endregion
}
Classes to convert:
Since isEqualTo
is used in like 90% of cases, should it be shorter (or at least have a shorter option?)
ex:
fun Assert<T>.eq(expected: Any?) { ... }
fun Assert<T>.neq(expected: Any?) { ... }
Long title! Basically the import sample at Maven is incorrect: https://mvnrepository.com/artifact/com.willowtreeapps.assertk/assertk-jvm/0.11
<dependency>
<groupId>com.willowtreeapps.assertk</groupId>
<artifactId>assertk-jvm</artifactId>
<version>0.11</version>
<type>pom</type>
</dependency>
Should be:
<dependency>
<groupId>com.willowtreeapps.assertk</groupId>
<artifactId>assertk-jvm</artifactId>
<version>0.11</version>
</dependency>
Or of type jar rather than pom. Otherwise the files load in, but compilation and testing don't work.
If the test list is longer than the actual list, it will only check the number of items in the actual list.
ex:
val actual = listOf(1, 2, 3)
assert(actual).containsExactly(1, 2, 3, 4)
passes
Given the Java class (with platform types):
class Parson { public String name; }
the following code
assert(new Person().name).isEqualTo(new Person().name)
picks the 'isEqualTo' from strings.kt, which requires not null type and fails with exception.
Adding a new method for the String?
type should resolve the problem.
fun Assert<String?>.isEqualTo(expected: String?)
Right now you can have assert(actual)
or assert("name", actual)
. Since name is optional, would it make more sense as the second param? assert(actual, "name")
. This may also play better with #12 since you could also have assert(actual, name = "name", message = "expected:foo but was:$actual")
I'm happy to contribute PRs and fix issues, but you guys are really slow at merging PRs and there hasn't been a release since August of last year, what is going on? Is this Repo dead?
the extension isNotNull { } gets the assertion as a parameter. So it is used this way:
assert("Hello").isNotNull{
it.contains("llo")
}
While other extensions, like all, get the assertion as the reciever. So it is used this way:
assert("Hello").all{
contains("llo")
}
Is there a good reason it is built this way?
It is possible to fix this by defining the isNotNull extension like this:
fun <T : Any> Assert<T?>.isNotNull(f: Assert<T>.() -> Unit = {}) {
if (actual != null) {
assert(actual!!, name = name).all(f)
} else {
expected("to not be null")
}
}
More of the exception stacktrace should be printed out for failures to make debugging easier.
For example:
mkobit.kt
import assertk.assertions.isEqualTo
fun main(args: Array<String>) {
assertk.assert {
someFunction()
}.returnedValue {
isEqualTo(2)
}
}
fun someFunction(): Int {
val two = 1 + 1
someOtherFunction()
return two
}
fun someOtherFunction() {
throw RuntimeException("thing")
}
Produces
Exception in thread "main" java.lang.AssertionError: expected value but threw:<java.lang.RuntimeException: thing>
at MkobitKt.main(mkobit.kt:6)
When just using
assertk.assert(someFunction()).isEqualTo(2)
Gives
Exception in thread "main" java.lang.RuntimeException: thing
at MkobitKt.someOtherFunction(mkobit.kt:14)
at MkobitKt.someFunction(mkobit.kt:9)
at MkobitKt.main(mkobit.kt:4)
Which is much easier to debug.
Probably this is more of a Intellij Issue, but perhaps somebody has solved it.
When I try to run the test cases from within IntelliJ 2017.2.5 via "Run Specs in", I got the following error message:
WARNUNG: TestEngine with ID 'spek' failed to discover tests
java.lang.NoSuchMethodError: org.junit.platform.engine.support.descriptor.ClassSource.from(Ljava/lang/Class;)Lorg/junit/platform/engine/support/descriptor/ClassSource;
at org.jetbrains.spek.engine.SpekTestEngine.resolveSpec(SpekTestEngine.kt:114)
at org.jetbrains.spek.engine.SpekTestEngine.resolveSpecs(SpekTestEngine.kt:74)
at org.jetbrains.spek.engine.SpekTestEngine.discover(SpekTestEngine.kt:50)
at org.junit.platform.launcher.core.DefaultLauncher.discoverEngineRoot(DefaultLauncher.java:130)
at org.junit.platform.launcher.core.DefaultLauncher.discoverRoot(DefaultLauncher.java:117)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
at org.jetbrains.spek.tooling.runner.junit.JUnitPlatformSpekRunner.run(JUnitPlatformSpekRunner.kt:107)
at org.jetbrains.spek.tooling.MainKt.main(Main.kt:58)
Any clue what to do about it?
Would it be worth it to setup e.g. Travis Integration for this repo?
I think it's really useful.
Since Kotlin can run on the JVM and Javascipt (and soon native), what about making assertk platform independent too ?
Specific platform assertion could be shipped in platform specific dependencies.
I would happily try this :)
What about changing isEqualTo signature ?
actually:
isEqualTo signature fun <T> Assert<T>.isEqualTo(expected: Any?)
proposal:
isEqualTo signature fun <T> Assert<T>.isEqualTo(expected: T)
In my case, It would help when refactoring code to have a more precise signature.
There seems to be support for generic arrays, but I don't see any support for specialized primitive arrays like ByteArrray
, IntArray
, ShortArray
, LongArray
, FloatArray
, DoubleArray
, CharArray
. I'd like all specialized array types to have official support.
AssertJ has the assertThatCode(() -> {}).doesNotThrowAnyException();
which can be useful for
This can sort of be done now by doing
assert {
maybeThrowingMethod()
}.returnedValue {}
In my own project I'm just using private fun <T> AssertBlock<T>.doesNotThrowAnyException() = returnedValue { }
But there might be a more idiomatic way to do it if it is desired as part of the library.
Error:(29, 17) Failed to resolve: com.willowtreeapps.assertk:assertk:0.9
assert(BigDecimal.ZERO).isZero()
fails.
More often than not I verify specific types of sealed classes. This leads to ugly code like this:
assert(someVar).isInstanceOf(Sealed.Actual::class)
assert((someVar as Sealed.Actual)).myCustomAssertion("some value")
Wouldn't it be nice if isInstanceOf
would return Assert<Sealed.Actual>
so this could be chained?
assert(someVar).isInstanceOf(Sealed.Actual::class).customAssertions("some", "values")
Even nicer would be to not have to write a custom assertion at all, but to be able to do something like this:
assert(someVar).isInstanceOf(Sealed.Actual::class).applyActual {
// `this` is `someVar` casted to `Sealed.Actual`
assert(field1).isEqualTo("some")
assert(field2).isEqualTo("values")
}
Opinions?
I don't know the right way to describe the feature, but this is something I use all the time from AssertJ.
Some from ObjectEnumerableAssert
to demonstrate what I'm looking for:
areAtLeast(int n, Condition<? super ELEMENT> condition)
areAtMost(int n, Condition<? super ELEMENT> condition)
areExactly(int n, Condition<? super ELEMENT> condition)
areNot(Condition<? super ELEMENT> condition)
are(Condition<? super ELEMENT> condition)
Collection currently has containsAll
which can have extra elements. It would be useful to have a method that doesn't allow extra elements for Collections. Map already has one: containsOnly
. I'd like to also use that for Sets or Lists.
List has containsExactly
which doesn't allow extra elements, but it also enforces ordering which might not always be wanted.
Bear with me:
I would expect the following code to be a failing test due to the call to fail()
on the last line in foo()
. However, the test passes. If I remove the throw
within the lambda passed to foo()
, then the call to fail()
works and the test fails.
The thrown exception in the lambda passed to foo()
shouldn't make a difference on the passing/failing of the test because it's caught within foo()
.
fun test() {
foo {
assert("This").all {
throw AssertionError()
}
}
}
fun foo(func: () -> Unit) {
try {
func()
} catch (e: Throwable) {
}
fail(AssertionError("Fail"))
}
assertAll {
assert(listOf(1, 2, 3)).containsExactly(5, 6, 7)
}
java.lang.AssertionError: The following 3 assertions failed:
New assertions to Map to check if it's empty. Right I have to do something like
assert(theMap.keys).isEmpty()
instead assert(theMap).isEmpty() would read better
See https://github.com/ota4j-team/opentest4j
One thing to consider also is will assertk support multiplatform project which may also influence this decision.
Currently, I do not see a method to assert that a string does not contains a specific substring.
I was thinking of having a method similar to the one offered by AssertJ, like this:
assert("test").doesNotContain("est")
It would be much easier to compare output if matching indexes were next to each other in the output, instead of:
at index:0 expected: ...
at index:1 expected: ...
at index:0 unexpected: ...
at index:1 unexpected: ...
do
at index:0 expected: ...
at index:0 unexpected: ...
at index:1 expected: ...
at index:1 unexpected: ...
This test case reproduces the error:
@Test
fun foo() {
assertThat(listOf("1", "2", "3")).containsExactly("2", "3", "1")
}
Both lists are of same size, contain the same elements in the same order except on the first element of the actual
list which is expected to be at the end of the list:
Caused by: java.lang.ArrayIndexOutOfBoundsException: -1
at assertk.assertions.support.ListDiffer.backtrack(ListDiffer.kt:87)
at assertk.assertions.support.ListDiffer.diff(ListDiffer.kt:10)
at assertk.assertions.ListKt.listDifferExpected(list.kt:48)
at assertk.assertions.ListKt.containsExactly(list.kt:44)
... 24 more
tests with assertk 0.13
On request in #70 OpenTest4j support was added, but that doesn't seem to exist in the classpath during test execution in IntelliJ:
java.lang.AssertionError: java.lang.NoClassDefFoundError: org/opentest4j/AssertionFailedError
at my.package.MyTest.shouldVerifySomething(MyTest.kt:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:600)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.robolectric.internal.SandboxTestRunner$2.evaluate(SandboxTestRunner.java:260)
at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:130)
at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:42)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.internal.SandboxTestRunner$1.evaluate(SandboxTestRunner.java:84)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.NoClassDefFoundError: org/opentest4j/AssertionFailedError
at assertk.FailureKt.fail(failure.kt:98)
at assertk.assertions.support.SupportKt.expected(support.kt:72)
at assertk.assertions.support.SupportKt.expected$default(support.kt:69)
at assertk.assertions.AnyKt$isInstanceOf$1.invoke(any.kt:209)
at assertk.Assert.transform(assert.kt:26)
at assertk.assertions.AnyKt.isInstanceOf(any.kt:204)
... 26 more
Caused by: java.lang.ClassNotFoundException: org.opentest4j.AssertionFailedError
at org.robolectric.internal.bytecode.SandboxClassLoader.getByteCode(SandboxClassLoader.java:163)
at org.robolectric.internal.bytecode.SandboxClassLoader.maybeInstrumentClass(SandboxClassLoader.java:118)
at org.robolectric.internal.bytecode.SandboxClassLoader.lambda$findClass$0(SandboxClassLoader.java:111)
at org.robolectric.util.PerfStatsCollector.measure(PerfStatsCollector.java:50)
at org.robolectric.internal.bytecode.SandboxClassLoader.findClass(SandboxClassLoader.java:110)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 32 more
When running the test outside of the IDE, e.g. through Gradle, the dependency is apparently there and the assertion is properly hit.
I'm using assertk-jvm 0.13.
This might be related to assertj/assertj#1072
It would be nice to have a .withFailMessage(...)
method to be able to customize failures.
fun Assert<View>.viewIsNotEmpty() {
assert(actual.childCount).withFailMessage("expected view to not be empty").isGreaterThan(0)
}
I want to check that some objects that don't have a proper equals method have the same content. What would be the best way of handling that? Assertj contains methods for field-by-field comparison, but I don't see anything like that here.
Writing a custom assertion for every such object requires a lot of boilerplate for the case when both are null, or one of them is not. I've created a helper method for it:
fun <T : Any> Assert<T?>.isEqualToNullSafe(expected: T?, nullSafeAssertion: Assert<T>.(nullSafeExpected: T) -> Unit) {
val actual = this.actual
if (actual == null && expected == null) {
return
}
if (actual != null && expected != null) {
assert(actual).nullSafeAssertion(expected)
} else {
fail(expected, actual)
}
}
with usage like that:
fun Assert<Bundle?>.isEqualTo(expected: Bundle?) {
isEqualToNullSafe(expected) { nullSafeExpected ->
assert(actual.keySet()).hasSameSizeAs(nullSafeExpected.keySet())
for (key in actual.keySet()) {
assert(actual.get(key)).isEqualTo(nullSafeExpected.get(key))
}
}
}
Maybe a similar method should be added to the library?
right now it's containsAll(vararg Any?)
which it makes it too easy to accidentally pass in listOf(foo)
instead of *listOf(foo)
fun Assert<ContentValues>.contains(vararg pairs: Pair<String, Any?>) {
These need to be implemented
This link in the README goes nowhere:
https://willowtreeapps.github.io/assertk/javadoc/assertk/assertk.assertions/index.html
I read that the built-in matchers are not supposed to be null-safe and a construct like the following should be used:
val nullString: String? = null
assert(nullString).isNotNull {
it.hasLength(4)
}
I find this highly unreadable and in some contexts even not properly usable. E.g. if you assert single things of an exception or regular value of an RxJava TestObserver
, the IDE will give you a "implicit it parameter of enclosing lambda is shadowed" warning:
someStream.test().assertError {
assert(it.message).isNotNull {
it.contains("some exception text")
}
true
}
So for me it would make much more sense to have a built-in Assert<String?>.contains()
or maybe even some kind of chaining that would change Assert<String?>
to Assert<String>
like this:
assert(it.message).isNotNull().contains("some exception text")
I guess chaining itself is the number one feature I miss in assertk, coming from assert-j.
Opinions?
Getting java.lang.AssertionError: java.lang.NoClassDefFoundError: org/opentest4j/AssertionFailedError
when running tests with Robolectric and test failure. Works fine when test assertion succeeds though. This wasn't an issue before with the same Robolectric version and 0.10.
Looks like a duplicate of #172.
I did try setting Preferences | Build, Execution, Deployment | Build Tools | Gradle | Runner | Run tests using: Gradle Test Runner
to no avail.
I am using IntelliJ IDEA 2019.1
But I did try running exactly the same spec as Instrumented Test and the problem was not reproducible.
Opening this issue to discuss #47. Open questions:
Remove the concept of 'soft assertions' that don't throw, instead all
assertions throw, and all
and assertAll
loop through lambda with a
try/catch. This removes the foot-gun of an assertion maybe running the
rest of the body when it fails, an allows props like index
and key
to return a result and fail with an exception instead of having to take
a lambda.
This has been done in a way to not cause existing code to fail to
compile. Instead, you may not see all exceptions thrown. Deprecated
functions have been kept to guide migration.
Would love some feedback on this, there's some pros/cons to this approach.
fail
and expected
always throws.isNotNull()
can return Assert<T>
for chaining. This means lambdas are not required for these.Instead of:
assert(foo).isNotNull { contains("bar") }
We can have:
assert(foo).isNotNull().contains("bar")
assertAll
in the same way.assertAll
/all
syntax is uglier
before:
assertAll {
assert(foo).isEqualTo("one")
assert(bar).isEqualTo("two")
}
after:
assertAll({
assert(foo).isEqualTo("one")
}, {
assert(bar).isEqualTo("two")
})
This likely means these features will be less-used.
Right now it prints out all items, this can make it hard to tell what's different. Should have some sort of diff view of the collection's contents based on the assertion.
I don't know if it's just the tests I'm writing lately, but I'm seeing a fair number of these kinds of failures lately:
org.opentest4j.AssertionFailedError: expected to contain exactly:
at index:2
Expected :null
Actual :null
<Click to see difference>
From an assertion like this:
val expectedValues = arrayOf( "ONE", "TWO" )
assertThat(Option.getByCompatibleFooType(Foo.BAR)).containsExactly(*expectedValues)
Neither the expected or actual arrays contain null, but the assertion error doesn't actually tell me what the problem is.
I added assertions for InputStream and Path and test cases for them.
You can find the pull request here: #36
Would a pull request enough to be merged, or is posting this issue the correct way?
Currently the message from Assert<Map<K,V>>.containsAll()
only tells which pairs were not found. It would be better if the message could enumerate what pair was expected and what it actually was.
I've had several teammates frequently run into confusion between assertk's assert
and the Kotlin stdlib assert
. It has been confusing for some team members to see import assertk.*
, have the imports folded in the editor, or when browsing the tests in the browser to have to cognitively differentiate the different call sites of assertk
.
I thought I would open this to see what the opinions are around the naming.
Getting this with assertk-jvm:0.11
, seems to be a known issue https://youtrack.jetbrains.com/issue/KT-23574. Workaround is to do
android {
packagingOptions {
exclude '/META-INF/main.kotlin_module'
}
}
as per https://youtrack.jetbrains.com/issue/KT-23695
we will probably have to update our kotlin version and release to fix it.
All assertions should be tested.
all
isNullOrEmpty
all
hasRooCause*
, hasStackTraceContaining
Currenly the lamdas in all
and index
have different signatures leading to strange exceptions:
class Event(val date:LocalDate = LocalDate.now)
fun test() {
val events = setOf(Event(), Event())
assert(events).all {
hasSize(2)
index(0) {
prop(Event::date)
.isEqualTo(LocalDate.of(2018, Month.December, 20))
}
}
}
leads to the following exception as the method prop
is not called in the implicit it
if the index
lamda but on this
of the all
extension lamda.
java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:71)
at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Instance.call(CallerImpl.kt:87)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:106)
at kotlin.jvm.internal.CallableReference.call(CallableReference.java:138)
at assertk.assertions.AnyJVMKt$prop$1.invoke(any.kt:74)
at assertk.assertions.AnyKt.prop(any.kt:143)
at assertk.assertions.AnyJVMKt.prop(any.kt:74)
[...]
My suggestion would be to make both lamdas an extension because thats the most intuitive.
This may be my lack of knowledge on kotlin-frontend-plugin, but when I use assertk with that plugin, during the build I get the following error, which I don't know how to resolve:
ERROR in ./node_modules_imported/assertk/assertk.js
Module not found: Error: Can't resolve 'opentest4k' in '/path/to/project/build/node_modules_imported/assertk'
@ ./node_modules_imported/assertk/assertk.js 3:4-56
I tried adding the npm module directly with:
kotlinFrontend {
npm {
devDependency "opentest4k"
}
but that errors saying it can't find the npm module:
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/opentest4k - Not found
npm ERR! 404
npm ERR! 404 'opentest4k@*' is not in the npm registry.
Any advice on using assertk with kotlin-frontend-plugin?
Proposal: add a couple methods to assert on either the line content or the bytes
Assert<Path>.lines(charset: java.nio.charset
= Charsets.UTF_8): Assert<List<String>>
Assert<Path>.bytes(): Assert<ByteArray>
When I'm trying the following example
fun Assert<Person>.hasAge(expected: Int) = given { actual ->
if (actual.age == expected) return
expected("age:${show(expected)} but was age:${show(actual.age)}")
}
assertThat(person).hasAge(10)
// -> expected age:<10> but was age:<18>
in a project with compile target 1.8 im getting following error:
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.