问题
I want validate an email with some code provided by Android.
Here is the code I want to mock :
if(!Patterns.EMAIL_ADDRESS.matcher(email).matches())
throw new InvalidPhoneException(phone);
In my test file :
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Patterns.class })
public class UserTest {
@Before
public void mockValidator() {
mockStatic(Patterns.class);
when(Patterns.EMAIL_ADDRESS.matcher(any(String.class)).matches()).thenReturn(true);
}
I got this error when I launch the tests :
java.lang.NullPointerException
at ch.mycompany.myapp.model.UserTest.mockValidator(UserTest.java:59)
EDIT 1 :
I tried :
mockStatic(Patterns.class);
Field field = PowerMockito.field(Patterns.class, "EMAIL_ADDRESS");
field.set(Patterns.class, mock(Pattern.class));
// prepare matcher
Matcher matcher = mock(Matcher.class);
when(matcher.matches())
.thenReturn(true);
// final mock
when(Patterns.EMAIL_ADDRESS.matcher(any(String.class)))
.thenReturn(matcher);
But when I do that, my code (Patterns.EMAIL_ADDRESS.matcher(email).matches()) return always false. This is confusing.
回答1:
you are performing the validation of the email field
you are not mocking the behaviour here when to return true or false. Also make a note we cannot mock final classes (Pattern).
when regex pattern matches with the value it returns true or false
Instead of complicating the things . Simply perform the valiation by passing the value.
Solution 1 :
@Test
public void test() throws Exception{
String email="hello@gmail.com";
System.out.println(Patterns.EMAIL_ADDRESS.matcher(email).matches());
}
protected static class Patterns{
private static final String EMAIL_PATTERN =
"^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
+ "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
private static final Pattern EMAIL_ADDRESS=Pattern.compile(EMAIL_PATTERN);
}
output :
true
Solution 2: Mocking the Matcher behaviour to return true or false
@RunWith(PowerMockRunner.class)
@PrepareForTest({Pattern.class,Matcher.class})
public class TestEmailPattern {
@Test
public void test() throws Exception{
String email="hello@gmail.com";
Pattern pattern=PowerMockito.mock(Pattern.class);
Matcher matcher=PowerMockito.mock(Matcher.class);
PowerMockito.when(matcher.matches()).thenReturn(true);
assertEquals(Patterns.EMAIL_ADDRESS.matcher(email).matches(),true);
}
protected static class Patterns{
private static final String EMAIL_PATTERN =
"^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
+ "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
private static final Pattern EMAIL_ADDRESS=Pattern.compile(EMAIL_PATTERN);
}
}
回答2:
Using Robolectric will save you lot of headaches in this kind of situation.
回答3:
You can mock final classes with Mockito 2 https://github.com/mockito/mockito/wiki/What's-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods
回答4:
In this particular case it is not necessary to mock the Patterns class with mockito (it is unmockable anyways), just change your source code as follows:
if(!Patterns.EMAIL_ADDRESS.matcher(email).matches())
throw new InvalidPhoneException(phone);
should be
if(!PatternsCompat.EMAIL_ADDRESS.matcher(email).matches())
throw new InvalidPhoneException(phone);
That did the job for me.
来源:https://stackoverflow.com/questions/41518223/mock-android-patterns-with-mockito