-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
This provides a Quality similar to `instanceOf` but also taking `Quality`s of subclasses of the given class. This is unsafe because there is no guarantee that the testee is actually of that subtype, but it may be required if the testee is a generic class. It solves issues like described in hamcrest/JavaHamcrest#388
- Loading branch information
Showing
4 changed files
with
179 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
...idence-core/src/main/java/org/saynotobugs/confidence/quality/object/UnsafeInstanceOf.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
* Copyright 2022 dmfs GmbH | ||
* | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package org.saynotobugs.confidence.quality.object; | ||
|
||
import org.dmfs.srcless.annotations.staticfactory.StaticFactories; | ||
import org.saynotobugs.confidence.Quality; | ||
import org.saynotobugs.confidence.quality.composite.AllOfFailingFast; | ||
import org.saynotobugs.confidence.quality.composite.QualityComposition; | ||
|
||
import static org.saynotobugs.confidence.description.LiteralDescription.NEW_LINE; | ||
|
||
|
||
@StaticFactories(value = "Core", packageName = "org.saynotobugs.confidence.quality") | ||
public final class UnsafeInstanceOf<T> extends QualityComposition<T> | ||
{ | ||
/** | ||
* A {@link Quality} that matches when the object under test is an instance of any subclass of the given class and | ||
* satisfies the given {@link Quality}. | ||
* <p> | ||
* This works like {@link InstanceOf} but provides fewer type-safety guarantees allowing you to pass | ||
* {@link Quality} of subtypes of {@code V}. This may be required when testing generic classes, because you'll | ||
* essentially be forced to work with raw types in such case. | ||
* | ||
* <h4>Example</h4> | ||
* <pre>{@code | ||
* Map actual = ...; | ||
* | ||
* assertThat(actual, unsafeInstanceOf(Map.class, Core.<Map<String, Object>>allOf( | ||
* containsEntry("k1", unsafeInstanceOf(Map.class, allOf( | ||
* containsEntry("k11", "v11"), | ||
* containsEntry("k12", "v12")))), | ||
* containsEntry("k2", unsafeInstanceOf(Iterable.class, iterates("v21", "v22"))), | ||
* containsEntry("k3", unsafeInstanceOf(String.class, equalTo("v3")))))); | ||
* }</pre> | ||
* <p> | ||
* <p> | ||
* Be aware that this also allows you to write nonsensical tests like this: | ||
* <pre>{@code | ||
* static class C1 {} | ||
* | ||
* static class C2 extends C1 { | ||
* int bar() {return 2;} | ||
* } | ||
* | ||
* @Test | ||
* void instanceOfTest() { | ||
* Object o = new C1(); | ||
* assertThat(o, unsafeInstanceOf(C1.class, has(C2::bar, equalTo(2)))); | ||
* } | ||
* }</pre> | ||
* <p> | ||
* In such case, when the Quality is not compatible with the actual type, the test will fail, reporting a | ||
* {@link ClassCastException}. | ||
*/ | ||
public <V extends T, Q extends V> UnsafeInstanceOf(Class<V> expectation, Quality<? super Q> delegate) | ||
{ | ||
super((Quality<T>) new AllOfFailingFast<>(NEW_LINE, new InstanceOf<>(expectation), delegate)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
...ce-core/src/test/java/org/saynotobugs/confidence/quality/object/UnsafeInstanceOfTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* | ||
* Copyright 2023 dmfs GmbH | ||
* | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package org.saynotobugs.confidence.quality.object; | ||
|
||
import org.dmfs.jems2.iterable.Seq; | ||
import org.junit.jupiter.api.Test; | ||
import org.saynotobugs.confidence.quality.composite.AllOf; | ||
import org.saynotobugs.confidence.quality.composite.Has; | ||
import org.saynotobugs.confidence.quality.grammar.That; | ||
import org.saynotobugs.confidence.quality.iterable.Iterates; | ||
import org.saynotobugs.confidence.test.quality.Fails; | ||
import org.saynotobugs.confidence.test.quality.HasDescription; | ||
import org.saynotobugs.confidence.test.quality.Passes; | ||
|
||
import static org.saynotobugs.confidence.Assertion.assertThat; | ||
|
||
class UnsafeInstanceOfTest | ||
{ | ||
|
||
@Test | ||
void testDelegate() | ||
{ | ||
assertThat(new UnsafeInstanceOf<>(Number.class, new That<>(new Has<>("intValue", Number::intValue, new EqualTo<>(1)))), | ||
new AllOf<>( | ||
new Passes<>(1, 1.001, 1L, 1f), | ||
new Fails<>(0.999, "(1) that had intValue <0>"), | ||
new Fails<>(2, "(1) that had intValue <2>"), | ||
new Fails<>("string", "(0) instance of <class java.lang.String>"), | ||
new Fails<>(new Object(), "(0) instance of <class java.lang.Object>"), | ||
new HasDescription("(0) instance of <class java.lang.Number>\n (1) that has intValue <1>") | ||
)); | ||
} | ||
|
||
@Test | ||
void testSubClassDelegate() | ||
{ | ||
assertThat(new UnsafeInstanceOf<>(Iterable.class, new That<>(new Iterates<>(1, "abc", true))), | ||
new AllOf<>( | ||
new Passes<>(new Seq<Object>(1, "abc", true), new Seq(1, "abc", true)), | ||
new Fails<>(new Seq<Object>(1.1, "abc", true), "(1) that iterated [ 0: <1.1>\n ... ]"), | ||
new Fails<>(2, "(0) instance of <class java.lang.Integer>"), | ||
new Fails<>("string", "(0) instance of <class java.lang.String>"), | ||
new Fails<>(new Object(), "(0) instance of <class java.lang.Object>"), | ||
new HasDescription("(0) instance of <interface java.lang.Iterable>\n (1) that iterates [ 0: <1>,\n 1: \"abc\",\n 2: <true> ]") | ||
)); | ||
} | ||
} |