Skip to content

Commit

Permalink
Support for anyNumberOf expectations (fixes #18) (#19)
Browse files Browse the repository at this point in the history
This is an expectation for JSON Array type. It checks whether all elements of the array match to the one given expectation. For example:

```
[1, 2, 1, 1].matches(anyNumberOf(1))    // fails - mismatch for `2`
[1, 1, 1, 1].matches(anyNumberOf(1))    // succeeds
```

Available for use via static factory-method `Expectations.anyNumberOf()`
  • Loading branch information
crizzis authored Feb 2, 2020
1 parent a4349ec commit 6a2a0d9
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.zendesk.jazon.expectation;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
class AnyNumberOf {

@Getter(AccessLevel.PACKAGE)
private final Object elementExpectation;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.zendesk.jazon.expectation;

import com.zendesk.jazon.MatchResult;
import com.zendesk.jazon.actual.*;
import com.zendesk.jazon.mismatch.MismatchWithPath;
import com.zendesk.jazon.mismatch.NullMismatch;
import com.zendesk.jazon.mismatch.TypeMismatch;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;

import java.util.ListIterator;

import static com.zendesk.jazon.MatchResult.failure;
import static com.zendesk.jazon.MatchResult.success;

@RequiredArgsConstructor
@EqualsAndHashCode
class ArrayEachElementExpectation implements JsonExpectation {

private final JsonExpectation expectationForEachElement;

@Override
public MatchResult match(ActualJsonNumber actualNumber, String path) {
return failure(typeMismatch(ActualJsonNumber.class, path));
}

@Override
public MatchResult match(ActualJsonObject actualObject, String path) {
return failure(typeMismatch(ActualJsonObject.class, path));
}

@Override
public MatchResult match(ActualJsonString actualString, String path) {
return failure(typeMismatch(ActualJsonString.class, path));
}

@Override
public MatchResult match(ActualJsonNull actualNull, String path) {
return failure(
new NullMismatch<>(this)
.at(path)
);
}

@Override
public MatchResult match(ActualJsonArray actualArray, String path) {
ListIterator<Actual> actualValues = actualArray.list().listIterator();
while (actualValues.hasNext()) {
Actual actualValue = actualValues.next();
MatchResult matchResult = actualValue.accept(expectationForEachElement, path + "." + actualValues.previousIndex());
if (!matchResult.ok()) {
return matchResult;
}
}
return success();
}

@Override
public MatchResult match(ActualJsonBoolean actualBoolean, String path) {
return failure(typeMismatch(ActualJsonBoolean.class, path));
}

private MismatchWithPath typeMismatch(Class<? extends Actual> actualType, String path) {
return new TypeMismatch(ActualJsonArray.class, actualType)
.at(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public JsonExpectation expectation(Object object) {
return expectedOrderedArray((List<Object>) object, this);
} else if (object instanceof Set) {
return expectedUnorderedArray((Set<Object>) object, this);
} else if (object instanceof AnyNumberOf) {
Object elementExpectation = ((AnyNumberOf) object).getElementExpectation();
return new ArrayEachElementExpectation(expectation(elementExpectation));
} else if (object == null) {
return new NullExpectation();
} else if (object instanceof Predicate) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.zendesk.jazon.expectation;

public class Expectations {

public static AnyNumberOf anyNumberOf(Object element) {
return new AnyNumberOf(element);
}
}
43 changes: 43 additions & 0 deletions jazon-core/src/test/groovy/com/zendesk/jazon/MatcherSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import com.zendesk.jazon.mismatch.*
import spock.lang.Specification
import spock.lang.Unroll

import java.util.function.Predicate

import static com.zendesk.jazon.expectation.Expectations.anyNumberOf

class MatcherSpec extends Specification {

static ActualFactory actualFactory = new ObjectsActualFactory()
Expand Down Expand Up @@ -449,6 +453,45 @@ class MatcherSpec extends Specification {
thrown(IllegalStateException)
}
@Unroll
def "array each element expectation: success"() {
expect:
match([a: anyNumberOf(expected)], [a: actual]).success()
where:
expected | actual
'1' | []
'1' | ['1']
'1' | ['1', '1']
true | [true]
2 | [2]
[b: true, c: 1] | [[[b: true, c: 1]]]
[3, 4, 5] | [[3, 4, 5]]
{ it -> it > 5 } as Predicate<Integer> | [6, 7, 8]
}
@Unroll
def "array each element expectation - element mismatch"() {
when:
def result = match([a: anyNumberOf(expected)], [a: actual])
then:
!result.ok()
result.mismatch().expectationMismatch() == elementMismatch
result.mismatch().path() == '$.a.' + path
where:
expected | actual || path | elementMismatch | _
1 | [1, 3, 1] || '1' | primitiveValueMismatch(1, 3) | _
1 | [1, 1, true] || '2' | new TypeMismatch(ActualJsonNumber, ActualJsonBoolean) | _
true | [true, 1, true] || '1' | new TypeMismatch(ActualJsonBoolean, ActualJsonNumber) | _
1 | [1, null, 1] || '1' | new NullMismatch<>(expectationFactory.expectation(1)) | _
[b: true, c: 1] | [[b: true, c: 2]] || '0.c' | primitiveValueMismatch(1, 2) | _
[3, 4, 5] | [[3, 4, false]] || '0.2' | new TypeMismatch(ActualJsonNumber, ActualJsonBoolean) | _
({ it -> it > 3 }
as Predicate<Integer>) | [4, 5, 2] || '2' | PredicateMismatch.INSTANCE | _
}
def "null expectation: fails for any present value"() {
when:
def result = match([a: null], [a: actual])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public JsonExpectation expectation(Object object) {
return expectedOrderedArray((List<Object>) object, this);
} else if (object instanceof Set) {
return expectedUnorderedArray((Set<Object>) object, this);
} else if (object instanceof AnyNumberOf) {
Object elementExpectation = ((AnyNumberOf) object).getElementExpectation();
return new ArrayEachElementExpectation(expectation(elementExpectation));
} else if (object instanceof Closure) {
Closure<Boolean> closure = (Closure<Boolean>) object;
return new PredicateExpectation(closure::call);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.zendesk.jazon.expectation.SpockExpectationFactory
import com.zendesk.jazon.mismatch.PredicateMismatch
import spock.lang.Specification

import static com.zendesk.jazon.expectation.Expectations.anyNumberOf

class MatcherForGroovySpec extends Specification {

MatcherFactory matcherFactory = new MatcherFactory(
Expand Down Expand Up @@ -158,4 +160,17 @@ class MatcherForGroovySpec extends Specification {
{ it.color == 'red' },
]
}

def "array each element expectation works correctly with lambda-style expectation"() {
when:
def result = matcherFactory.matcher()
.expected([a: anyNumberOf {it -> it > 5}])
.actual([a: [6, 7, 8, 9, 0]])
.match()

then:
!result.ok()
result.mismatch().expectationMismatch() == PredicateMismatch.INSTANCE
result.mismatch().path() == '$.a.4'
}
}

0 comments on commit 6a2a0d9

Please sign in to comment.