Java 网站的JUnit测试包含一个字符串或(独占)其他字符串
在spring mvc项目中,我对索引/主页的内容进行了测试:Java 网站的JUnit测试包含一个字符串或(独占)其他字符串,java,testing,junit,hamcrest,Java,Testing,Junit,Hamcrest,在spring mvc项目中,我对索引/主页的内容进行了测试: @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class HomePageTest { @Autowired private MockMvc mockMvc; @Test public void shouldContainStrings() throws Exception { t
@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"))));
....
但这也不起作用(如果两个字符串都出现在页面中,则不会给出错误)。只要方法接受Hamcrest matcher,我在这里看到两个选项:
匹配器匹配器=
全部(
是(containsString(“登录”)。或(containsString(“注销”)),
是(不是(allOf(containsString(“登录”),containsString(“注销”));
断言(“_Login”,matcher);//好啊
断言(“_Logout”,matcher);//好啊
断言(“_Login_Logout_”,matcher);//失败
断言(“_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu失败
就我个人而言,我更喜欢使用第二种选择。当我找不到合适的时候,我不得不亲自写信给custom matcher
@Test
public void containsOneOfTwoSubStringsExclusive() {
assertTrue((mainString.contains(substring1) && !mainString.contains(substring2)) ||
(!mainString.contains(substring1) && mainString.contains(substring2)))
}
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 );
}
}
import java.util.function.BiConsumer;
导入javax.annotation.Nonnull;
导入org.hamcrest.BaseMatcher;
导入org.hamcrest.Description;
导入org.hamcrest.Matcher;
导入org.hamcrest.core.CombinableMatcher;
/**
*类似于{@link CombinableMatcher.combinableethermatcher},但仅当只有一个给定的
*匹配器{@link Matcher#匹配(对象)}。
*
*@作者bugorskia
*/
公共类EitherXorMatcher扩展BaseMatcher{
//_**字段**_//
@非空
私人最终配对者<?超级T>配对者;
@非空
私人最终匹配者<?超级T>B匹配者;
//_**内部阶级**_//
/**
*这仅适用于builder模式/fluent界面。
*/
公共静态最终类EitherXorMatcherBuilder{
//_**字段**_//
@非空
private final matcher我认为您应该有两种测试方法:一种是因为您没有登录而期望“登录”,另一种是因为您没有登录而期望“注销”。@davidxxx:这一点很好。但让我们假设它们是不同的字符串(而不是“登录/注销”)。是否有方法匹配此XOR,或者我必须编写自己的匹配程序?
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 );
}
}