For 1., you could have a non-zero number type, or you could settle for a check in the JobExtra constructor. For 2., it sounds like this is a coproduct situation - a JobExtra is either certifiedFormanRelated or has an appliedPercentage. Scala doesn't quite have native support for coproducts the way it does for tuples, but you could represent it with a sealed class hierarchy:
sealed trait ForemanOrPercentage
case class ForemanRelated() extends ForemanOrPercentage
case class AppliedPercentage(value: Decimal) extends ForemanOrPercentage
or being a bit lazier you could just use an Option[Decimal] and say that if it's Some it's the applied percentage and if it's None then it's certified foreman related.In Java there isn't a "sealed" keyword, but you can emulate it with the visitor pattern - you'd define a ForemanOrPercentageVisitor and only ever operate on ForemanOrPercentages via the visitor. That way if someone creates their own subtype of ForemanOrPercentage it still has to behave like one or the other, because they still have to implement visit().
Since the user input modifies this object, the user can put the object into an invalid state. In fact, when the object is first created it is in an invalid state. So I can't simply define my types so that invalid state is impossible.
That's very interesting, and shows how types can go much further then what we see in languages like Java. I'll have to try out one of these languages some day and see how well it works in practice.
No, I don't think so. I'll write a test that uses the library. If the library returns null when I don't expect it, it'll go into my code an cause an exception to tbe thrown. I'm not deliberately testing for nulls, I'm testing generally that my code does what I think it does.
The kind of library function that returns null doesn't usually return it on the "happy path", it usually returns null for some weird condition you haven't thought of. I mean, if it's a function that returns null often or that you'd expect to sometimes return null, you'll wrap up the return value in an option straight away even without having written a test. The kind of library function that causes you to get exceptions in production is the kind that returns null in some weird corner case - but unless you think of the weird corner case, you wouldn't hit it in a test either.
If I was using a library with some complex stateful interface I might write tests for the interaction with that (I'd probably put a facade on it, keeping it separate from the business logic, and the facade would have unit tests) - but I'd try to avoid using such a library. And I'll always have a few high-level end-to-end tests (not really "unit" tests) as a basic "does it work at all", because that kind of mistake is easy to make. But the bread-and-butter unit tests that I wrote when I worked in Python just don't seem valuable or necessary.
Honestly, I can't say how it would work if I was using a java code base that consistently applied these kinds of ideas. You may be right, but at this point I have hard time believing.
Ah, Scala. I could be prepared to believe that this is true for Scala (and similarly advanced typed systems). It is when people claim they write fewer tests in java than in python that I found it really dubious.
> Yeah, happy to if you're interested. It's a useful exercise in thinking it through for myself as much as anything.
Ok here's another test:
These are action objects, they represent a sequence of user actions on a JobExtra.
I construct the final state by reducing across all those actions I verify that the job extra is considered valid afterwards. To be considered valid:1. The changeAmount must be set to non-zero 2. The certifiedFormanRelated must be false or the appliedPercentage must be true
But, if the user modifies the appliedPercentage, the certifiedFormanRelated should be automatically set to true.
So this, and a number of related tests verify that under different sequences of actions I end up the result I expected.
Is there a way to push this into the type system?