102

Ruby's Test::Unit has a nice assert_matches method that can be used in unit tests to assert that a regex matches a string.

Is there anything like this in JUnit? Currently, I do this:

assertEquals(true, actual.matches(expectedRegex));
Josh Glover
  • 25,142
  • 27
  • 92
  • 129

10 Answers10

115

If you use assertThat() with a Hamcrest matcher that tests for regex matches, then if the assertion fails you'll get a nice message that indicates expected pattern and actual text. The assertion will read more fluently also, e.g.

assertThat("FooBarBaz", matchesPattern("^Foo"));

with Hamcrest 2 you can find a matchesPattern method at MatchesPattern.matchesPattern.

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
pholser
  • 4,908
  • 1
  • 27
  • 37
  • 23
    Hamcrest 2.0 has the `Matchers.matchesPattern(String)` now built-in: https://github.com/hamcrest/JavaHamcrest/blob/master/hamcrest-library/src/main/java/org/hamcrest/Matchers.java#L1401 – hinneLinks Mar 07 '16 at 09:21
  • 5
    Permalink for what @hinneLinks refers to: https://github.com/hamcrest/JavaHamcrest/blob/8f9372a152612b45f51a12d1c549643fdac5492a/hamcrest-library/src/main/java/org/hamcrest/Matchers.java#L1474-L1480 – pioto Feb 20 '17 at 21:47
58

No other choice that I know. Just checked the assert javadoc to be sure. Just a tiny little change, though:

assertTrue(actual.matches(expectedRegex));

EDIT: I have been using the Hamcrest matchers since pholser's answer, check that out too!

StackzOfZtuff
  • 2,534
  • 1
  • 28
  • 25
Miquel
  • 15,405
  • 8
  • 54
  • 87
  • 1
    Ah yes, `assertTrue()` is definitely nicer. I blame Eclipse's auto-complete for me not knowing about that. ;) – Josh Glover Dec 14 '11 at 13:31
  • 4
    `assertTrue` can't give you as much detail as `assertEquals` or `assertThat` when a test fails – Mike Valenty Jul 12 '12 at 17:02
  • 2
    @Michael Sure it can. `assertTrue("Expected string matching '" +expectedRegex+ "'. Got: "+actual, actual.matches(expectedRegex));`. It's not as nice as Hamcrest though. – MikeFHay Sep 24 '13 at 10:57
  • @MikeValenty If you're just comparing a value to `is(true)`, then `assertThat` doesn't give you any more detail than `assertTrue` does. To get proper error messages, you need a different matcher (or you construct the message manually as @MikeFHay suggested). – ThrawnCA Jun 12 '17 at 05:32
  • Important: string.matches() is available from version 8 of java and in that version it doesn't support multiline, even using the respective flag (?m); then you'd need to do it in the old verbose way: Pattern.compile() then matcher.find() – Ismael Sarmento Aug 04 '21 at 16:07
22

You can use Hamcrest, but you have to write your own matcher:

public class RegexMatcher extends TypeSafeMatcher<String> {

    private final String regex;

    public RegexMatcher(final String regex) {
        this.regex = regex;
    }

    @Override
    public void describeTo(final Description description) {
        description.appendText("matches regex=`" + regex + "`");
    }

    @Override
    public boolean matchesSafely(final String string) {
        return string.matches(regex);
    }


    public static RegexMatcher matchesRegex(final String regex) {
        return new RegexMatcher(regex);
    }
}

usage

import org.junit.Assert;


Assert.assertThat("test", RegexMatcher.matchesRegex(".*est");
Ralph
  • 118,862
  • 56
  • 287
  • 383
18

You can use Hamcrest and jcabi-matchers:

import static com.jcabi.matchers.RegexMatchers.matchesPattern;
import static org.junit.Assert.assertThat;
assertThat("test", matchesPattern("[a-z]+"));

More details here: Regular Expression Hamcrest Matchers.

You will need these two dependencies in classpath:

<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-core</artifactId>
  <version>1.3</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-matchers</artifactId>
  <version>1.3</version>
  <scope>test</scope>
</dependency>
Abdull
  • 26,371
  • 26
  • 130
  • 172
yegor256
  • 102,010
  • 123
  • 446
  • 597
4

Because I was also looking for this functionality, I have started a project on GitHub called regex-tester. It's a library that helps ease testing regular expressions in Java (only works with JUnit currently).

The library is very limited right now, but it does have a Hamcrest matcher that works like this

assertThat("test", doesMatchRegex("tes.+"));
assertThat("test", doesNotMatchRegex("tex.+"));

More about how to use regex-tester is here.

Nick A. Watts
  • 819
  • 10
  • 16
4

A matcher similar to Ralph's implementation has been added to the official Java Hamcrest matchers library. Unfortunately, it's not yet available in a release package. The class is on GitHub though if you want a look.

Nick A. Watts
  • 819
  • 10
  • 16
4

another alternative using assertj. this approach is nice as it allows you to pass the pattern object directly.

import static org.assertj.core.api.Assertions.assertThat;
assertThat("my\nmultiline\nstring").matches(Pattern.compile("(?s)my.*string", Pattern.MULTILINE));
abe
  • 331
  • 1
  • 6
3

There is corresponding matcher in Hamcrest: org.hamcrest.Matchers.matchesPattern(String regex).

As development of Hamcrest stalled you can't use latest available v1.3:

testCompile("org.hamcrest:hamcrest-library:1.3")

Instead you need to use new dev series (but still dated by Jan 2015):

testCompile("org.hamcrest:java-hamcrest:2.0.0.0")

or even better:

configurations {
    testCompile.exclude group: "org.hamcrest", module: "hamcrest-core"
    testCompile.exclude group: "org.hamcrest", module: "hamcrest-library"
}
dependencies {
    testCompile("org.hamcrest:hamcrest-junit:2.0.0.0")
}

In test:

Assert.assertThat("123456", Matchers.matchesPattern("^[0-9]+$"));
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
  • I get error: Duplicate class org.hamcrest.BaseDescription found in modules jetified-hamcrest-core-1.3.jar (org.hamcrest:hamcrest-core:1.3) and jetified-java-hamcrest-2.0.0.0.jar (org.hamcrest:java-hamcrest:2.0.0.0) – Alexei Nov 28 '19 at 09:54
3

An update for JUnit 5, without any extra library.

Now you can use assertLinesMatch as described in https://junit.org/junit5/docs/5.0.1/api/org/junit/jupiter/api/Assertions.html#assertLinesMatch-java.util.List-java.util.List-

The assertion was created to match several lines of text, so you need to provide a List even if you try to match only one line. For e.g.:

assertLinesMatch(List.of("Expected at the beginning.*"), List.of(contentUnderTest));

The assertion algorithm will try an exact match and then, if it fails, will use String.match interpreting the expected value as a regular expression.

An important note: if your contentUnderTest contains several lines, then the .* in the regex won't work (. doesn't match a new line character by default) so you may need to add an embedded flag (https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#DOTALL):

// Flag ?s means match new lines
assertLinesMatch(List.of("(?s)Expected at the beginning.*"), List.of(contentWithMultipleLines));
Dharman
  • 30,962
  • 25
  • 85
  • 135
Uyric
  • 646
  • 7
  • 17
2


it's not JUnit but here is another way with fest-assert :

assertThat(myTestedValue).as("your value is so so bad").matches(expectedRegex);
boly38
  • 1,806
  • 24
  • 29