Hi. Sorry for the dumb question but I don't see th...
# arrow
d
Hi. Sorry for the dumb question but I don't see this explicitly covered in the documentation. Currently, I'm using kotlin-result library to model my error domain (basically the same as
Either
). I'm trying to convert this to the Raise DSL but I'm getting quite a hairy code at places where I eventually need to handle the error. This is especially visible in tests where I often hard-assume the ok result. Currently, I have this:
Copy code
class MyService {
  fun someMethod(): Result<Int, MyError> = Ok(1)
}

fun test() {
  val result = MyService().someMethod().get()
  // check result
}
With the Raise DSL, I have this:
Copy code
class MyService {
  fun Raise<MyError>.someMethod(): Int = 1
}

fun test() {
  val result =
    with(MyService()) {
      recover({someMethod()}) { error("not expected") }
    }
  // check result
}
Isn't there a better way? (Note that the two ways are not exactly equivalent since in the first case the type of
result
is
Int?
whereas in the second it's
Int
. This is not so important in tests, however.)
e
I think I would do
Copy code
fun test() {
  val result = either { MyService().someMethod() }.shouldBeRight()
}
shouldBeRight()
is a Kotest Arrow assertion which asserts that the result is OK and unpacks it.
d
I don't use kotest (yet) and I surely need to somehow bring in the service receiver but the
either
way looks certainly better.
Copy code
fun test() {
  val result = either { 
    with(MyService()) { someMethod() }
  }.getOrNull()
}
I think I will probably make a small test helper out of this.
e
You could also let your method return an Either, and skip using the Raise context.
Copy code
class MyService {
  fun someMethod(): Either<MyError, Int> =  either { 1 }
}

fun test() {
  val result = MyService().someMethod().getOrNull()
}
🙂
d
Well, as I said "I'm trying to convert this to the Raise DSL" 😄
y
nullable { ignoreErrors { } }
is likely along the lines of what you want. I've also used a
shouldSucceed { }
function for tests before, or even a parameter injector thingy from JUnit (can't remember the name) that injects an
object : Raise<Any?> { override fun raise(r: Any?) = error("Test failed with $r") }
d
Good ideas! I will probably go with that custom
testRaise
object since
nullable { ignoreErrors { } }
silently swallows all errors. I don't see, however, what's the advantage of using the ParameterResolver (probably that JUnit thingy) over simply using a (statically) defined object. In either case, you have to do
with(testRaise) { ... }
anyway.
y
It's because you can have your desired
Raise<Blah>
as a receiver or a context on your test method! E.g. :
Copy code
class MyTestClass {
  @Test
  context(_: Raise<MyError>, _: Raise<MyOtherError>)
  fun fooTest() {
    // Both available in context!
  }
}
That's because receivers and contexts all compile down to parameters!
d
Then I probably misunderstood what you were talking about. I thought you meant this:
Copy code
@ExtendWith(TestRaiseParameterResolver.class)
class MyTestClass {
  @Test
  fun fooTest(testRaise: TestRaise) {
    // ...
  }
}
With this I couldn't see the advantage of having the static object injected by JUnit.
Oh, I understand now! The beauty of context parameters is such that I strongly consider using them in production code even if they are experimental 🙂
K 1
p
I've been using something like this where needed:
Copy code
inline fun <E, R> shouldNotRaise(block: Raise<E>.() -> R): R =
    fold(block, { fail("Unexpected raise of $it") }, ::identity)

inline fun <E> shouldRaise(block: Raise<E>.() -> Any?): E =
    fold(block, ::identity) { fail("Expected raise, but returned $it") }
the latter could be made reified to include the raise type in the error message as desired
r
I'm late to the party, but if you need to test results that use Arrow types and you have assertj as a dependency, you can use https://github.com/rcardin/assertj-arrow-core