Junit: assert that a list contains at least one property that matches some condition
Asked Answered
W

4

5

I have a method that will return a list of objects of type MyClass. MyClass has many properties, but I care about type and count. I want to write a test that asserts that the returned list contains at least one element that matches a certain condition. For instance, I want at least one element in the list of type "Foo" and count 1.

I'm trying to figure out how to do this without literally looping over the returned list and checking each element individually, breaking if I find one that passes, like:

    boolean passes = false;
    for (MyClass obj:objects){
        if (obj.getName() == "Foo" && obj.getCount() == 1){
            passes = true;
        }
    }
    assertTrue(passes);

I really don't like this structure. I'm wondering if there's a better way to do it using assertThat and some Matcher.

Watanabe answered 8/10, 2018 at 1:15 Comment(0)
G
5

with hamcrest imports

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

you can test with

    assertThat(foos, hasItem(allOf(
        hasProperty("name", is("foo")),
        hasProperty("count", is(1))
    )));
Gasket answered 8/10, 2018 at 2:35 Comment(4)
Will this assert that at least one item has both properties, or that there is at least one item that has each property? I.e. does this ensure it's the same item that has both?Watanabe
Checks that there is AT LEAST 1 item in the list with name=="foo" AND that the same item has count==1Gasket
Looks like org.hamcrest.Matchers isn't found by my IDE. What's the maven repository I need for this? I figured it would be this, but it's notWatanabe
I am indeed using v1.3Gasket
S
4
assertTrue(objects.stream().anyMatch(obj ->
    obj.getName() == "Foo" && obj.getCount() == 1
));

Or more likely:

assertTrue(objects.stream().anyMatch(obj ->
    obj.getName().equals("Foo") && obj.getCount() == 1
));
Sybyl answered 8/10, 2018 at 1:36 Comment(0)
F
1

I don't know if it's worth using Hamcrest for this but it's good to know it's out there.

public class TestClass {
    String name;
    int count;

    public TestClass(String name, int count) {
        this.name = name;
        this.count = count;
    }

    public String getName() {
        return name;
    }

    public int getCount() {
        return count;
    }
}

@org.junit.Test
public void testApp() {
    List<TestClass> moo = new ArrayList<>();
    moo.add(new TestClass("test", 1));
    moo.add(new TestClass("test2", 2));

    MatcherAssert.assertThat(moo,
            Matchers.hasItem(Matchers.both(Matchers.<TestClass>hasProperty("name", Matchers.is("test")))
                    .and(Matchers.<TestClass>hasProperty("count", Matchers.is(1)))));
}
Foreknowledge answered 8/10, 2018 at 2:32 Comment(0)
R
0

Although more verbose, you can do it with AssertJ only (i.e. without Hamcrest). Use these imports:

import static org.assertj.core.api.Assertions.allOf;
import static org.assertj.core.api.Assertions.anyOf;
import static org.assertj.core.api.Assertions.assertThat;
import org.assertj.core.api.Condition;

Then you can assert like this:

assertThat(foos).haveAtLeastOne(
    allOf(
        new Condition<>(x -> x.getName() == "Foo", ""),
        new Condition<>(x -> x.getCount() == 1, "")));

This format might be useful when you have more complicated conditions and you prefer to have each in its own line, like for example finding one item that matches at least one set of conditions:

assertThat(foos).haveAtLeastOne(
    anyOf(
        allOf(
            new Condition<>(x -> x.getName() == "Foo", ""),
            new Condition<>(x -> x.getCount() == 1, "")),
        allOf(
            new Condition<>(x -> x.getName() == "Bar", ""),
            new Condition<>(x -> x.getCount() == 2, ""))));
Rerun answered 18/4 at 0:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.