66 / 75 lines covered (88.0%)
1unit module BDD::Behave::Matcher::Change;23use BDD::Behave::Matcher;45class ChangeMatcher does Matcher is export {6 has &.observable is required;7 has $.before-value is rw;8 has $.after-value is rw;9 has Bool $.callable-given is rw = True;10 has Bool $.action-ran is rw = False;11 has $.expected-from is rw;12 has Bool $.has-from is rw = False;13 has $.expected-to is rw;14 has Bool $.has-to is rw = False;15 has $.expected-by is rw;16 has Bool $.has-by is rw = False;17 has $.expected-by-at-least is rw;18 has Bool $.has-by-at-least is rw = False;19 has $.expected-by-at-most is rw;20 has Bool $.has-by-at-most is rw = False;21 has Str $.miss-reason is rw;2223 method matches($actual --> Bool) {24 $!before-value = Nil;25 $!after-value = Nil;26 $!action-ran = False;27 $!miss-reason = Str;28 $!callable-given = ?($actual ~~ Callable);29 return self.check-captured unless $!callable-given;3031 $!before-value = &!observable.();32 $actual.();33 $!action-ran = True;34 $!after-value = &!observable.();3536 self.check-captured;37 }3839 method has-by-modifier(--> Bool) {40 ?($!has-by || $!has-by-at-least || $!has-by-at-most);41 }4243 method check-captured(--> Bool) {44 unless $!callable-given {45 $!miss-reason = 'non-callable';46 return False;47 }4849 if $!has-from && !($!before-value eqv $!expected-from) {50 $!miss-reason = 'from';51 return False;52 }5354 if $!has-to && !($!after-value eqv $!expected-to) {55 $!miss-reason = 'to';56 return False;57 }5859 if $!before-value eqv $!after-value {60 $!miss-reason = 'no-change';61 return False;62 }6364 if self.has-by-modifier {65 unless $!before-value ~~ Real && $!after-value ~~ Real {66 $!miss-reason = 'by-non-numeric';67 return False;68 }69 my $delta = $!after-value - $!before-value;70 if $!has-by && !($delta == $!expected-by) {71 $!miss-reason = 'by';72 return False;73 }74 if $!has-by-at-least && !($delta >= $!expected-by-at-least) {75 $!miss-reason = 'by-at-least';76 return False;77 }78 if $!has-by-at-most && !($delta <= $!expected-by-at-most) {79 $!miss-reason = 'by-at-most';80 return False;81 }82 }8384 $!miss-reason = Str;85 True;86 }8788 method change-clause(--> Str) {89 my $clause = '';90 $clause ~= ' from ' ~ $!expected-from.raku if $!has-from;91 $clause ~= ' to ' ~ $!expected-to.raku if $!has-to;92 $clause ~= ' by ' ~ $!expected-by.raku if $!has-by;93 $clause ~= ' by at least ' ~ $!expected-by-at-least.raku if $!has-by-at-least;94 $clause ~= ' by at most ' ~ $!expected-by-at-most.raku if $!has-by-at-most;95 $clause;96 }9798 method delta(--> Mu) {99 return Nil unless $!before-value ~~ Real && $!after-value ~~ Real;100 $!after-value - $!before-value;101 }102103 method failure-message($actual --> Str) {104 given $!miss-reason {105 when 'non-callable' {106 return "expected a Callable for change, but got " ~ $actual.raku;107 }108 when 'from' {109 return "expected block to change observable" ~ self.change-clause110 ~ ", but it started as " ~ $!before-value.raku;111 }112 when 'to' {113 return "expected block to change observable" ~ self.change-clause114 ~ ", but it ended as " ~ $!after-value.raku;115 }116 when 'by' | 'by-at-least' | 'by-at-most' {117 return "expected block to change observable" ~ self.change-clause118 ~ ", but it changed by " ~ self.delta.raku;119 }120 when 'by-non-numeric' {121 return "expected block to change observable" ~ self.change-clause122 ~ ", but values were not numeric (before: "123 ~ $!before-value.raku ~ ", after: " ~ $!after-value.raku ~ ")";124 }125 }126 "expected block to change observable" ~ self.change-clause127 ~ ", but it remained " ~ $!before-value.raku;128 }129130 method failure-message-negated($actual --> Str) {131 "expected block not to change observable" ~ self.change-clause132 ~ ", but it changed from " ~ $!before-value.raku133 ~ " to " ~ $!after-value.raku;134 }135136 method description(--> Str) { 'change observable' ~ self.change-clause }137138 method expected-value(--> Mu) {139 return [$!expected-from, $!expected-to] if $!has-from && $!has-to;140 return $!expected-from if $!has-from;141 return $!expected-to if $!has-to;142 return $!expected-by if $!has-by;143 return $!expected-by-at-least if $!has-by-at-least;144 return $!expected-by-at-most if $!has-by-at-most;145 Nil;146 }147}