-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved testkit #26
Comments
@ittaiz @uriavraham @noam-almog your input would be invaluable here... |
@holograph def beValid: Matcher[ ResultMatcher ]
def beInvalid: Matcher[ ResultMatcher ]
def beInvalidWith( violations: Matcher[ Set[ Violation ] ] ): ResultMatcher
def beInvalidWith( violations: Matcher[ Violation ]* ): ResultMatcher As for the rest, I rather inspect matchers on their natural environment, do you have any examples of those matchers being used somewhere ? side comment: |
+1 for @noam-almog's request for usage, I was thinking the same thing. "Person Validations" should {
"fail a person with an empty first name" in new ctx {
val personWithEmptyFirstName = Person().with(name = "")
validate(personWithEmptyFirstName) must beViolation("firstName" -> "must not be empty")
}
} |
I have a couple of qualms with that idea:
As for @noam-almog's question, unless I misunderstand, List( 1, 2, 3 ) should contain( allOf( 2,3 ) )
List( 1, 2, 3 ) should not( containTheSameElementsAs( List( 2, 3 ) ) ) |
Adding more options that does the same thing is something common with specs, there are always matchers with different method names to make them more readable in several usages for example: I don't think it needs to be separated to different traits. @holograph , when I started writing stuff I realized that we started on the wrong foot. The code that you sent makes us debate over this code vs another code where the questions that we need to ask are different.
My answers would be:
This three matchers can be translated to code somehow (with some syntax we can pick) validate(SomeClass(p1 = "", p2 = "", p3 = "") must beInvalidWith(violations = "p1", "p3")
validate(SomeClass(p1 = "", p2 = "", p3 = "") must beInvalid
validate(SomeClass(p1 = "", p2 = "", p3 = "") must beInvalidWith(violations = "p1" -> "p1 is invalid", "p3" -> "p3 is a null")
validate(SomeClass(p1 = "", p2Text = "", p3 = "") must beInvalidWith(violationsThat = contain("Text")) Now we can switch the the beInvalidWith (mine) with failWith (yours) or keep them both... Btw, I don't like that we write the property names as strings which are not directly connected to the actual member of the class (val or def) but I don't see any way around this. Btw2, descriptions are flawed because basically nobody can actually know what the description is without running the validating and then copy paste it to the test. This is what I do in the hub (and I know @ittaiz will hate me forever for this) but I would like to find a way to be able to know what the description is before the test to make this TDD compatible. Btw3, I think I gave you enough reading materials for the whole weekend.... :) |
Interesting discussion. |
@ittaiz def isTextField: Matcher[String] = contain("Text")
validate(SomeClass(p1 = "", p2Text = "", p3 = "") must beInvalidWith(violationsThat = isTextField) As for tests for this, I discussed this with a few ppl back in the day and it was decided that those validators won't need tests. As @holograph put it, 'you wouldn't test the OR mapping for your OR mapper'. I write tests for those validators because I don't trust (forgive me Tomer) accord entirely and I think I should test it's validators and just because I'm on kind of an auto pilot sometimes. As for partial, I meant that an object failed on p1, and p2, when I match against p1 only it should fail because it didn't fail on p2, failures should match entirely or fail. It's the current assumption in the matchers and I think it should remain the same but still I felt it needed to be specified. |
I think that we do “test the OR mapper” in the sense that we write Integration Tests which describe what we need out of the tool and what we expect it to do (or in other words to see that we use it correctly). I know that you do not subscribe to integration tests but that doesn’t mean I don’t want the ability to do such tests. There is a question of whether we should use the tool’s test kit for these tests as it might be lying too (or we misunderstand the kit as well). @sagyr WDYT? BTW, I think I am thinking big I just think a complex object should be composed of other complex objects and so on and that each part should be tested. Doing God tests is hard and unpleasant. Ittai Zeidman On Sun, Nov 16, 2014 at 12:24 PM, Noam Almog notifications@github.com
|
As I mentioned earlier, there is no strict guideline to test all object validators on each project, and one should rely on this FW without thoroughly test each validator. What I found that on the TDD scale there are more relaxed ppl and more strict, you for example are strict, other ppl said that it doesn't need tests at all, I'm somewhere in the middle. Not sure what you are saying that I 'do not subscribe to integration tests' or anything you said after that. This test kit is intended to test the validators on our unit tests. Do I use it, yes, Do I think everyone should use it, I'm not sure, it depends. |
I was referring to DAO tests — On Sun, Nov 16, 2014 at 5:01 PM, Noam Almog notifications@github.com
|
If the customer wants the product to reject certain inputs with an error I have several options. One would be to validate it manually, yet another might be using some sort of a tool or library. Whatever my choice is for implementing the requirement - it all starts with a failing test. |
I've started more seriously looking at this; I believe the ideal syntax would be something like: // Test domain --
case class Container(f: String)
case object Container {
implicit val v = validator[Container] { _.f is notEmpty }
}
case class Test(f1: String, f2: Int, coll: Seq[Container])
case object Test {
implicit val v = validator[Test] { t =>
t.f1 is notEmpty
t.f2 should be > 5
t.coll.each is valid
}
}
// Assertions --
val result = validate(Test("", 3, Seq(Container("Good"), Container(""))))
result shouldBe aFailure
// Macro-driven property descriptions per @ittaiz:
result should failOn(_.f1)
// Negation
result shouldNot failOn(_.f1)
// Optional constraint matching; values are implicitly expanded to be_===
result should failOn(_.f1 -> "must not be empty")
// Optional value matching
result should failOn(_.f2 -> 3 -> "got 3, must be greater than 5")
// Composing Specs2/ScalaTest matchers
result should failOn(_.f2 -> be_>=(2))
// Matching on group (nested) violations
result should failOn(_.coll because (Container("") -> "is not valid") because (_.f -> "must not be empty"))
// Multiple matches (variadic) with implicit /AND/
result should failOn(_.f1, _.f2, _.coll)
// "Only" modifier for exhaustive checks
result should failOn(_.f1).only This will require a pretty substantial change to the result model though, since Thoughts? (pinging @ittaiz @sagyr @noam-almog @hugebdu) |
👍 I prefer the beValid notation but other than that, looks gr8. |
Unfortunately this (and any variant on the syntax) can never work. The problem is the way the Scala type inference work. Consider the following expression: result should failOn(_.f1 -> "must not be empty") The crux of the problem is that A different syntax could be result should failOn { t: Test =>
t.f1
t.f2 -> 3 -> "got 3, must be greater than 5"
t.coll because ( Container( "" ) -> "must not be empty" )
} Requiring a brand new DSL for matching violations, to me, feels pretty abusive of Accord's users. What do you think? |
Well for the time being, I'm reducing the scope of this feature: basically, switch to using native matchers (for whichever testing library you end up using) in a backwards-compatible manner. |
The Accord testkit (for both Specs2 and ScalaTest) is both woefully underpowered and fails the DRY principle. Specifically, it makes it very hard to use native matchers, and impossible to compose rules freely. For example, with the existing framework it's impossible to assert that a result is a failure, and that all violations must match a specific rule. In short, a better testkit is necessary.
Following is prototype code for a new testkit based on Specs2:
This testkit is mostly source-compatible with existing test code based on
accord-specs2
, but still needs some work on the API and available matchers. Comments welcome.The text was updated successfully, but these errors were encountered: