简体   繁体   中英

AnyString() as parameter for unit test

I have to deal with a legacy application that has no tests. So before I begin refactoring I want to make sure everything works as it is.

Now imagine the following situation:

public SomeObject doSomething(final OtherObject x, final String something) {
    if(x == null) throw new RuntimeException("x may not be null!");
    ...
}

Now I want to test that null check, so to be sure it works and I don't lose it once I refactor.

So I did this

@Test(expected = RuntimeException.class)
public void ifOtherObjectIsNullExpectRuntimeException() {
    myTestObject.doSomething(null, "testString");
}

Now, this works of course.

But instead of "testString" I'd like to pass in a random String.

So I tried with:

@Test(expected = RuntimeException.class)
public void ifOtherObjectIsNullExpectRuntimeException() {
    myTestObject.doSomething(null, Mockito.anyString());
}

But this is not allowed., as I get

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: ... You cannot use argument matchers outside of verifications or stubbing

I do understand the meaning of this, but I wonder whether I can still manage to do what I want without parameterizing my test or the like. The only libraries I may use are Junit, AssertJ, Mockito and Powermock.

Any ideas?

Tests should be deterministic. Using random values in a test makes it difficult to reproduce behavior when debuging a failed test. I suggest that you just create a String constant for the test such as "abcdefg" .

Well, like Mockito is trying to tell you via that exception, that's not really how you'd use anyString . Such methods are only to be used by mocks.

So, why not try testing with an actual random string? My personal favorite in such a scenario: java.util.UUID.randomUUID() .toString() . This will virtually always generate a brand new string that has never been used for your test before.

I'd also like to add that if you are writing tests for your SomeObject class that you should avoid mocking SomeObject 's behavior. From your example, you weren't exactly doing that, but it looked like you might be going down that route. Mock the dependencies of the implementation you're trying to test, not the implementation itself! This is very important; otherwise you aren't actually testing anything.

You are mixing up concepts here.

All those "mocking" helpers like anyString() are meant to be used when configuring a mock object.

But when you check your testing code:

@Test(expected = RuntimeException.class)
public void ifOtherObjectIsNullExpectRuntimeException() {
  myTestObject.doSomething(null, "testString");
}

you will find: there is absolutely no mocking involved for this test. You simply can't use those Mockito calls in that place; because "there is no Mockito" in that place.

And just for the record - no need to go overboard here anyway. Your logic is very clear here: when the first argument is null, then you throw that exception. Thus it really doesn't matter at all what comes in as second argument. So thinking for an hour how to test null with any second argument is, well, in my eyes: waste of your time.

Final hint: there is java.lang.Objects And that class has a nice check for null, so my production code only looks like

public SomeObject doSomething(final OtherObject x, final String something) {
  Objects.requireNonNull(otherObject, "otherObject must not be null");
  Objects.requireNonNull(something, "something must not be null");

Only difference there: requires... throws NullPointerExceptions

Final finally: some people suggest to put final on every parameter; but I wouldn't do that. It adds no value in 99% of all cases. It just means that you have more code to read; for no good reasons. But that is a question of style.

EDIT on the comment about having a test to check for potential future changes: you shouldn't do that:

  1. To a certain degree, how your input is verified is an implementation detail. You don't test for implementation details. In other words:
  2. Your method has a certain contract (that you, for example specify informally by writing a javadoc that says "throws NPE on null input"). Your tests should verify exactly that current contract. And the contract is: throws if first argument is null.

And maybe another point of view; as I still think you are wasting your time here! You should make sure that all your interfaces are clear, easy to understand, and easy to use. That they allow users of your code to do the right thing easily; and prevent him from doing wrong things. That is what you should focus on - the quality of your interfaces as a whole! So instead of worrying how you could write a test for potential future changes; just make sure that your code base is overall consistent.

Well i do not have much knowledge of mockito but you can always create your own random string generator. maybe that can work and u can modify more types of inputs in it

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM