Should I test toString() with junit? - java

Can such tests have a good reason to exist?

Some classes use toString for more than just user-readable informative string. Examples are StringBuilder and StringWriter. In such a case it is of course advisable to test the method just like any other business-value method.
Even in the general case it is good practice to smoke-test toString for reliability (no exceptions thrown). The last thing you need is a log statement blowing up your code due to an ill-implemented toString. It has happened to me several times, and the resulting bugs are of the nastiest kind, since you don't even see the toString call in the source code—it's implicitly buried inside a log statement.

The question is not should I test toString(), but do you care about the result of toString()? Is it used for something? If so, then yes, test it.
If a method gets used for something real, then test it.

Obvious answer is „no, it's just a waste of time“. But for many classes, first of all value-wrappers, toString should be overloaded and deliver more information that just org.package.ClassName#2be2befa
So my propostal test for toString is:
#Test
public final void testToString() {
assertFalse(new MyClass().toString().contains("#"));
}
It also increases test converage what is at least not bad.

If the result of the method is important to you, you should test it, otherwise you can just ignore that.

I am going to go against the general advise and say testing a toString method definitely has its place. Applications I have work on log a lot, especially if you turn on debug or trace level logs. If I am relying on the logs to help identify a bug and some fields from my POJO are not present because some developer forgot to regenerate the toString method, this is a huge setback!
The problem is that the toString method is an absolute pain to test as their is no fixed format or a clear way to test it. I would recommend not writing a test yourself, but using a library such as ToStringVerifier
#Test
public void testToString()
{
ToStringVerifier.forClass(User.class).verify();
}

Related

good practice for testing mocked object

It is good practise to match mock objects widely but verify them precisely.
for example:
Using this:
when(myMock.has(any())).thenReturn(myValue);
Rather than:
when(myMock.has(eq("blah")).thenReturn(myValue);
Along with:
var result = myMethod();
assertThat(result, is(myValue));
Because it is making sure that it is always returned myValue regardless of the has method input.
There was a good explanation for this rule but I can not find it.
something along the lines: match widely and verify precisely.
It would be great if you can advise me about the name of the rule or some reference to it?
The explanation is quite simple: It will make your live easier.
Imagine the case that a caller will not call your method with "blah". In this case you rely on the mocking framework what will be returned, most likely null, zero or false. Your test will then run into a different direction or even fail with a NullpointerException. For other developers it will be hard to understand what went wrong here.
If you match widely, your test will continue as expected, but you should place a verification afterwards that makes the test fail with a clean reason. Developers tend to omit the verification step, wich renders the test useless quite often.
Usually there is no reason to match on a precise parameter value, except for the case when you want your mock to act differently on two values.
Most frameworks provide methods for the method call verification, e.g. Mockito:
#Mock
private Repository repository;
#Test
private void testReadData() {
Mockito.when(repository.findById(any())).thenReturn(yourEntity);
// run your test
Mockito.verify(repository).findById("foo");
}

How to test method that wrapping another method?

Let's imagine having class (written in Java-like pseudocode):
class MyClass {
...
public List<Element> getElementsThatContains(String str) {
return this.getElementsThatContains(new Set<String> { str });
}
public List<Element> getElementsThatContains(Set<String> strs) {
...
}
}
First of all - I have getElementsThatContains(Set<String> strs) properly 100% covered.
How should I cover getElementsThatContains(String str):
Should I copy (almost) all the tests but with call to getElementsThatContains(String str)?
Should I just make one test
method that check if results from first and second methods are same
(with same incoming data)?
Should I refactor my code so I do not have
such a situation? (If yes, how?)
Yes, you should cover both methods. The reason for having unit tests is the safety net, when the code is refactored. For example, Someone might refactor the implementation of 'getElementsThatContains(String str)' and it will always return an empty List. Despite getElementsThatContains(Set strs) has 100% coverage those tests won't catch this.
No, you should not make one test method that check if results from first and second methods are same. This is generally considered a bad practice. Moreover, if there is a bug in one method, your test would just check the other method returns same incorrect result.
No, you should not copy all the tests, because the test cases for each method would be different. The arguments for the methods are different. So you will have different test cases for each, despite that underneath the same method is called.
Yes you should test both methods, and you should use distinct test cases for each method.
But you should care less for your line coverage.
Don't get me wrong here! It is important to keep the line coverage high. But it is more important to have 100% behavior coverage. And if you come across untested lines your question should be: "Is this untested code needed (i.e. what requirement does it implement) or is it obsolete?".
When we write our tests with line coverage in mind we tend to focus on the implementation details of our code under test. In consequence our tests are likely to fail when we change this implementation details (e.g. during refactoring). But our tests should only fail if the tested behavior changes and not when we change the way this behavior is achieved.

Is it ok to add toString() to ease debugging?

I work a lot in intellij and it can be quite convenient to have classes having their own tostring(the generated one in intellij works fine) so you can see something more informative than MyClass#1345 when trying to figure out what something is.
My question is: Is that ok? I am adding code that has no business value and doesn't affect my test cases or the execution of my software(I am not using toString() for anything more than debugging). Still, it is a part of my process. What is correct here?
The toString() method is mainly designed as a debugging purpose method.
Except some exceptional cases, you should favor its use for debug purposes and not to display information to the clients as client needs may happen to be different or be the same as the toString() method today but could be different tomorrow.
From the toString() javadoc, you can read :
Returns a string representation of the object. In general, the
toString method returns a string that "textually represents" this
object. The result should be a concise but informative representation
that is easy for a person to read. It is recommended that all
subclasses override this method.
The parts that matter for your are :
The result should be a concise but informative representation
that is easy for a person to read.
and
It is recommended that all
subclasses override this method.
You said that :
Still, it is a part of my process. What is correct here?
Good thing : the specification recommends it.
Besides the excellent points by davidxxx, the following things apply:
Consistency matters. People working with your code should not be surprised by what is happening within your classes. So either "all/most" classes #override toString() using similar implementations - or "none" does that.
Thus: make sure everybody agrees if/how to implement toString()
Specifically ensure that your toString() implementation is robust
Meaning: you absolutely have to avoid that your implementation throws any exception (for example a NPE because you happen to do someString + fieldX.name() for some fieldX that might be null).
You also have to avoid creating an "expensive" implementation (for example code that does a "deep dive" into some database to return a value from there).
2 cent of personal opinion: I find toString() to be of great value when debugging things; but I also have seen real performance impacts by toString() too expensive. Thing is: you have no idea how often some trace code might be calling toString() on your objects; so you better make sure it returns quickly.
The docs explain the function of this method:
Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.
As you see, they don't specify a perticular use for this method or discourage you from using it for debuging, but they only state what it is expected to do and also recomend implementing this method in subclasses of Object.
Therefore strictly speaking how you use this method is up to you. In the university course i am taking, overwriting the toString method is required for some tasks and in some cases we are asked to use it to demonstrate debuging.
It is perfectly OK and even a good idea. Most classes don't specify the content of toString so it's not wise to use it for logic (the content may change in a future version of the class). But some classes do, for example StringBuilder. And then it is also OK to use the return value for logic.
So for your own classes you may even opt to specify the content and use (and let your users use) the return value for logic.

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:
To a certain degree, how your input is verified is an implementation detail. You don't test for implementation details. In other words:
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

spirit of a jUnit test

Suppose that you have the following logic in place:
processMissing(masterKey, masterValue, p.getPropertiesData().get(i).getDuplicates());
public StringBuffer processMissing(String keyA, String valueA, Set<String> dupes) {
// do some magic
}
I would like to write a jUnit test for processMissing, testing its behavior in event dupes is null.
Am i doing the right thing here? Should I check how method handles under null, or perhaps test method call to make sure null is never sent?
Generally speaking, what is the approach here? We can't test everything for everything. We also can't handle every possible case.
How should one think when deciding what tests to write?
I was thinking about it as this:
I have a certain expectation with the method
Test should confirm define my expectation and confirm method works under that condition
Is this the right way to think about it?
Thanks and please let me know
First, define whether null is a valid value for the parameter or not.
If it is, then yes, definitely test the behavior of the method with null.
If it is not, then:
Specify that constraint via parameter documentation.
Annotate that constraint on the parameter itself (using an annotation compatible with the tool below).
Use a static analysis tool to verify that null is never passed.
No unit test is required for the invalid value unless you're writing code to check for it.
The static analysis tool FindBugs supports annotations such as #NonNull, with some limited data-flow analysis.
I personally think it would be unnecessarily expensive within large Java codebases to always write and maintain explicit checks for NULL and corresponding, non-local unit tests.
If you want to ensure that people don't call your API with a null argument you may want to consider using annotations to make this explicit, JSR 305 covers this, and its used in Guava. Otherwise you're relying on users reading javadoc.
As for testing, you're spot on in that you can't handle every possible case, assuming you don't want to support null values, I'd say that you may want to throw an IllegalArguemntException rather than a NullPointerException so you can be explicit about what is null, then you can just test for that exception being thrown - see JUnit docs.

Categories

Resources