Tests with random inputs can be much easier to write. For example here's a test for a single hard-coded example:
public bool canSetEmail() {
User u = new User(
"John",
"Smith",
new Date(2000, Date.JANUARY, 1),
new Email("john@example.com"),
Password.hash("password123", new Password.Salt("abc"))
);
String newEmail = new Email("smith@example.com");
u.setEmail(newEmail);
return u.getEmail().equals(newEmail);
}
Phew! Here's a randomised alternative:
public bool canSetEmail(User u, Email newEmail) {
u.setEmail(newEmail);
return u.getEmail().equals(newEmail);
}
Not only is the test logic simpler and clearer, it's much more general. As an added bonus, when we write the data generators for User, Email, etc. we can include a whole load of nasty edge cases and they'll be used by
all of our tests. I've not used Java for a while, but in Scalacheck I'd do something like this:
def genEmail = {
val genStr = arbitrary[String]
.map(_.replace("@", ""))
.retryUntil(_.nonEmpty)
for {
user <- genStr
domain <- genStr
tlds <- Gen.listOf(genStr)
} yield (user + "@" + (domain :: tlds).mkString("."))
}
Even using the default String generators, this still gives pretty good examples, e.g.
scala> genEmail.sample
res1: Option[String] = Some(뀩貲㳱誷Ⓣ壟獝鈂ᗘ䮕鹍尛斿績線孞왁궽偔ቫ쥑瞴䒨䏵艥꿊狿냩쩈簋㷡泪伺鑬鮘䦚벛妹乘饡㙧ꐤ큫ᯕ肳铬呢ூ靪틯鏱滄㧄P莶寕상䐀鹭ᚿ㌤ᇹ䶂攔ꅥ㶓⽌ꅂ뀏뛶盏⍸㇗ɧ⇽줓嵛@퇐䎁鱎馉琿䏍㔰蘿⠶㘮큵휕炠᭑㯠ꇷ氕瑤镦碋䓯鄓헛㥝籆찷舊⸦䁜⯞௵籋㟨㨴鯅鿸刡㽴ﮈ耾刏碓辁亁ᦨ氦ব꧓ꌝ飖䒱찮刍ฤ⯛ஃ颰溯쨚ই媐䄇延醴熜䢐필㉕徫澬폁횱坠줊埬າ⟂.蹵箅䀈떥喹뭡㘺ꐟ⯉쉂㯻牏䢺梘쭸칹总⻎恵꣥ﴈ宎嶲ꎮ쌊䯧挤ꓯ⻯ꢠ鏿㰔操駚樅졇੩풻殜ᶝ팱.瘽៣뿔뱡䞿愝滠蛊妏뷩먰⦹挸긖᩿㣽ﮀ嫶ꚸ㗃鈴䇡⋥梏ڝ晡烡.줼欺ࠞ娫)
The other advantage is that automated shrinking does a pretty good job of homing in on bugs. For example, if our test breaks when there's no top-level domain (e.g. 'root@localhost') then (a) that will be found pretty quickly, since 'tlds' will begin with a high probability of being empty and (b) the other components will be shrunk as far as possible, e.g. 'user' and 'domain' will shrink down to a single null byte (the "smallest" String which satisfies out 'nonEmpty' test); hence we'll be told that this test fails for \0@\0 (the simplest counterexample). We'll also be given the random seeds used by the generators.