junit测试包含一个字符串或(独占的)其他字符串

rbl8hiat  于 2021-07-09  发布在  Java
关注(0)|答案(3)|浏览(282)

在spring mvc项目中,我对索引/主页的内容进行了测试:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HomePageTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldContainStrings() throws Exception {
        this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
            .andExpect(content().string(containsString("Hello World")));
    }
}

到目前为止,这个测试还不错。但是现在我想测试字符串“login”或(excl)“logout”是否出现,也就是说,我想测试这两个字符串中是否只有一个(不是零,也不是两个)出现在内容中。我怎样才能符合这个条件?
我试过了

...
.andExpect(content().string(
      either(containsString("Login")).or(containsString("Logout"))));
....

但这也不起作用(如果两个字符串都出现在页面中,则不会给出错误)。

ebdffaop

ebdffaop1#

当我找不到一个合适的时候,我不得不亲自写信给客户匹配者。

import java.util.function.BiConsumer;

import javax.annotation.Nonnull;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.core.CombinableMatcher;

/**
 * Similar to the {@link CombinableMatcher.CombinableEitherMatcher} but only passes if <em>only one</em> of the given
 * matchers {@link Matcher#matches(Object)}.
 *
 * @author bugorskia
 */
public class EitherXorMatcher<T> extends BaseMatcher<T>{

    //_**FIELDS**_//

    @Nonnull
    private final Matcher< ? super T > aMatcher;

    @Nonnull
    private final Matcher< ? super T > bMatcher;

    //_**INNER CLASS**_//

    /**
     * This is just for the builder pattern/fluent interface.
     */
    public static final class EitherXorMatcherBuilder<T>{

        //_**FIELDS**_//

        @Nonnull
        private final Matcher<? super T> aMatcher;

        //_**CONSTRUCTOR**_//

        private EitherXorMatcherBuilder( @Nonnull final Matcher<? super T> aMatcher ){
            this.aMatcher = aMatcher;
        }

        //_**API METHODS**_//

        @Nonnull
        public Matcher<T> xor( @Nonnull final Matcher<? super T> anotherMatcher ){
            return new EitherXorMatcher<>( aMatcher, anotherMatcher );
        }

    }

    //_**CONSTRUCTOR**_//

    private EitherXorMatcher( @Nonnull final Matcher< ? super T > aMatcher, @Nonnull final Matcher< ? super T > bMatcher ){
        this.aMatcher = aMatcher;
        this.bMatcher = bMatcher;
    }

    @Nonnull
    public static <T> EitherXorMatcherBuilder<T> exclusivelyEither( final Matcher<? super T> aMatcher ){
        return new EitherXorMatcherBuilder<>( aMatcher );
    }

    @Nonnull
    public static <T> Matcher<? super T> exclusivelyEither( @Nonnull final Matcher<? super T> aMatcher, @Nonnull final Matcher<? super T> bMatcher ){
        return new EitherXorMatcher<>( aMatcher, bMatcher );
    }

    @Nonnull @Deprecated
    public static <T> EitherXorMatcherBuilder<T> either( final Matcher<? super T> aMatcher ){
        return exclusivelyEither( aMatcher );
    }

    //_**API METHODS**_//

    @Override
    public boolean matches( final Object item ){
        final boolean aMatches = aMatcher.matches( item );
        final boolean bMatches = bMatcher.matches( item );

        return xor( aMatches, bMatches );
    }

    @Override
    public void describeTo( final Description description ){
        description.appendText( "Either { " );
        aMatcher.describeTo( description );
        description.appendText( " } xor { " );
        bMatcher.describeTo( description );
        description.appendText( " } " );
    }

    @Override
    public void describeMismatch( final Object item, final Description description ){
        final boolean aMatches = aMatcher.matches( item );
        final boolean bMatches = bMatcher.matches( item );
        assert !xor( aMatches, bMatches ): "Should not have gotten called!";
        assert aMatches == bMatches: "This is implied, and more of a developer comment than a runtime check.";

        final BiConsumer<Matcher<? super T>,Description> describer;
        final String startWord, joinWord;
        if( aMatches ){
            startWord = "Both";
            joinWord = "and";
            describer = Matcher::describeTo;
        }else{
            startWord = "Neither";
            joinWord = "nor";
            describer = ( m, d ) -> m.describeMismatch( item, description );
        }

        description.appendText( startWord ).appendText( " { " );
        describer.accept( aMatcher, description );
        description.appendText( " } " ).appendText( joinWord ).appendText( " { " );
        describer.accept( bMatcher, description );
        description.appendText( " } " ).appendText( " matched instead of exactly one." );
    }

    //_**HELPER METHODS**_//

    private static boolean xor( final boolean aMatches, final boolean bMatches ){
        // xor :: one or the other but not both
        return ( aMatches || bMatches )  &&  ! ( aMatches && bMatches );
    }

}
huwehgph

huwehgph2#

只要 string() 方法接受hamcrest matcher,我在这里看到两个选项:
或者自己实现类似xor的matcher(您可以使用这个答案作为参考)https://stackoverflow.com/a/29610402/1782379)...
…或使用复杂的条件,如“其中任何一个,但不能同时使用”

Matcher<String> matcher =
        allOf(
                is(either(containsString("Login")).or(containsString("Logout"))),
                is(not(allOf(containsString("Login"), containsString("Logout")))));
assertThat("_Login_", matcher); // OK
assertThat("_Logout_", matcher); // OK
assertThat("_Login_Logout_", matcher); // FAIL
assertThat("__", matcher); // FAIL

我个人更喜欢第二种选择。

p4tfgftt

p4tfgftt3#

@Test
public void containsOneOfTwoSubStringsExclusive() {
    assertTrue((mainString.contains(substring1) && !mainString.contains(substring2)) || 
        (!mainString.contains(substring1) && mainString.contains(substring2)))
}

相关问题