47

I'm testing a method to see if it returns the correct string. This string is made up of a lot of lines whose order might change, thus usually giving 2 possible combinations. That order is not important for my application.

However, because the order of the lines might change, writing just an Assert statement will not work, since sometimes it will pass the test, and sometimes it will fail the test.

So, is it possible to write a test that will assert an actual string value against 2 or more expected string values and see if it is equal to any of them?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Alex
  • 7,432
  • 20
  • 75
  • 118

15 Answers15

69

Using the Hamcrest CoreMatcher (included in JUnit 4.4 and later) and assertThat():

assertThat(myString, anyOf(is("value1"), is("value2")));
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • I managed to fix this eventually, but for some reason, for me, JUnit 4 doesn't have an 'assertThat' method in CoreMatcher. I ended up combining the main Hamcrest library with the JUnit library in order to get it to work. – Alex May 17 '11 at 13:37
  • Not for me...I just see assertTrue – Alex May 17 '11 at 13:43
  • 1
    @Andrei: are you sure you checked `org.junit.Assert` and not `junit.framework.Assert`? The later only exist for backwards compatibility with JUnit 3 and doesn't support `assertThat. In `org.junit.Assert` was introduced in JUnit 4.4. So if you have an earlier version, it will be missing. – Joachim Sauer May 17 '11 at 13:47
  • You missed one ')' at the end of `assertThat` statement. – JiaHao Xu Aug 27 '20 at 11:21
  • 1
    @JiaHaoXu: indeed, fixed now. Note that you can edit (or suggest an edit) the answer yourself for typos like this. – Joachim Sauer Aug 27 '20 at 12:20
  • Please add the relevant import statements to the example, to make it more clear. – Thomas Schütt Jul 09 '21 at 09:43
34

I would use AssertJ for this:

import static org.assertj.core.api.Assertions.*;

assertThat("hello").isIn("hello", "world");

It's more concise and it will give you a descriptive message when the assertion fails.

Jonathan
  • 20,053
  • 6
  • 63
  • 70
16

I am using the following, I hope this would help:

String expectedTitles[] = {"Expected1","Expected2"};
List<String> expectedTitlesList = Arrays.asList(expectedTitles);

assertTrue(expectedTitlesList.contains((actualTitle)));
Barun Kumar
  • 161
  • 1
  • 3
  • for who use junit Assert `String expectedTitles[] = {"In-Progress","Completed"};` `List expectedTitlesList = Arrays.asList(expectedTitles);` `Assert.assertTrue(expectedTitlesList.contains((transferRequest.getRequestStatus())));` – Extreme Jun 25 '17 at 21:58
  • 1
    For those who is forced to use junit 3 this is a good solution. – sergeyan Nov 26 '19 at 13:21
7

You can use Hamcrest for this:

assertThat(testString, anyOf(
    containsString("My first string"), 
    containsString("My other string")));

(I see Joachim just answered very similarly (+1)... i'll add this as another example.)

alpian
  • 4,668
  • 1
  • 18
  • 19
6

I read all answers, but the one that seems most compact and expressive to me is using Hamcrest's isOneOf which is already included in JUnit

assertThat(result, isOneOf("value1", "value2"));

which gives you a nice error message when failing.

java.lang.AssertionError: 
Expected: one of {"value1", "value2"}
     but: was "value"
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
[...]
fglez
  • 8,422
  • 4
  • 47
  • 78
4

If your using junit I'd just do something like the following:

assertTrue(myString.equals("Value1") || myString.equals("Value"));
Tom Jefferys
  • 13,090
  • 2
  • 35
  • 36
  • 8
    This is a bad practice in testing. Though logically, it will pass and fail when it's supposed to, the problem is that if it fails, you will not have a descriptive error message. instead, you will get something like "expected true but was false" which tells you nothing about the problem. But by using hamcrest matchers or assertj, you will get something like this on failure : expected to be one of the following : "Value1", "Value2" but was "XYZ" – Franz See Mar 16 '18 at 17:43
2

Consider writing a custom hamcrest matcher returned by a method, in this case containsOneOf, i.e.:

assertThat(myString, containsOneOf("value1", "value2"));

In keeping with the "xUnit patterns" you should avoid conditional logic in your matcher, a loop with a break statement should suffice.

Have a look at Hamcrest and xUnit Patterns for more information.

Jonathan
  • 20,053
  • 6
  • 63
  • 70
murungu
  • 2,090
  • 4
  • 21
  • 45
1

I think you can just use assertTrue:

assertTrue(testString.matches("string1|string2"));

OSrookie
  • 13
  • 3
1

If the content for a line is fixed you can split it at line endings before comparing. Then simply compare each line to a set of expected values. Something like this:

   Set<String> expectedValues = ...
   String[] split = text.split("\n");
   for(String str : split) {
      assert(expectedValues.contains(str));
   }

If you want to check that all expected values are present you can assert that expectedValue.remove(str) == true and assert after the loop that the Set is empty. If some lines may occur multiple times you have to use a Bag instead of a Set.

joe776
  • 1,106
  • 14
  • 23
1

The simplest/most efficient might be

assert str.equals(result1) || str.equals(result2);

This will effectively have no overhead when you don't have assertions turned on.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 1
    but in Android studio, I am finding that asserts are not evaluated in my instrumented tests. I need to use assertTrue instead – likejudo Aug 26 '16 at 17:28
  • @likejiujitsu you need to ensure -ea is a command line option. assertEquals is best for testing but only use assert in production so it can be turned off. – Peter Lawrey Aug 26 '16 at 22:21
  • `assert.assertequals(expected, actual)` doesn't allow `||` operator – Sanchit Mar 07 '18 at 05:10
  • @Sanchit assetTrue does. – Peter Lawrey Mar 07 '18 at 23:26
  • 1
    But this will not show you any meaningful error message when it fails. Using hamcrest or assertj provides you with something like "expected values X, Y but was Z" – Franz See Mar 16 '18 at 17:45
0

Assuming the method under test returns an array, you could test using Hamcrest's arrayContainingInAnyOrder.

assertThat(result, is(arrayContainingInAnyOrder("value1", "value2", "value")))

Note: use of is wrapper is purely optional, used only as readability sugar.

Chris Willmore
  • 574
  • 3
  • 13
0

Simple solution using AssertTrue.
Just create a List.of() the expected values and check is it contains the expected value.

assertTrue(List.of("expected_1", "expected_2").contains("actual))
Neel Alex
  • 605
  • 6
  • 10
0

If you do not have or can't use third-party libraries except junit core, you can use this small helper method:

public static void assertListContains(List<Object> expected, Object actual) {
    if (!expected.contains(actual)) {
        fail("Expected: " + expected + " Actual: " + actual);
    }
}

Advantage, in contrast to other workaround answers, is that you can see what was expected and what was actual:

org.opentest4j.AssertionFailedError: Expected: [1, 2] Actual: 3
Cmyker
  • 2,318
  • 1
  • 26
  • 29
-1

It's an old post, but I wanted to have an native easy answer, without having extra libraries to add. :)

So I did :

String e
e = "ear"
j = "jars"

assert (j == "jar" || e == "ear")

or if you want them to be both true

assert (j == "jar" && e == "ear")
-1

This seems to me like the simplest solution ...

assert obtainedValue in [acceptedValue1, acceptedValue2]
Albion
  • 135
  • 5