Lets say I have a method:
someMethod(X anObject)
Where X is a type of object that is extremely complex. By this I mean it is not something one can easily instantiate on the fly. I need to somehow unit test someMethod, but I cannot so simply create an X object to put in as parameters.
So I first think to try and mock the object, but the problem I run in to is the someMethod function calls many methods of anObject, meaning this X object that is being mocked has a latge amount of functions that need to be called, and thus need to be mock-expected. To make things worse, these X object methods being called return more X objects, meaning I have to mock objects, to expect mock method calls, to return more mock objects.
Regarding this scenario I have a few questions, as I'm new to the concept of unit testing:
The lengthy unit test method aside, I find my unit test to not only be testing as to whether a method works or not, but also specifying the implementation (because I'm basically specifying most of the code that is being called in the method itself with the mock-expects). Is this a problem (mostly to the concept of unit testing itself)?
Is there any way to get around this, even if only to make my unit test methods be a lot less verbose and more maintainable?
I thought about taking a serialized X object from somewhere else, saving that, and then whenever I call my unit test method, I would unserialize my X object and run that as parameters. This is just some random idea I thought of the top of my head; does anyone actually do this?
In case anyone is wondering what exactly I'm doing, I'm using the IDebugContextListener interface to grab debugging information regarding data on a stackframe at a given step on the java debugger. The "X" that I am referring to are objects that are defined by the interface here, including objects such as IValue, IVariable, and IStackframe. All these variables are provided to me by the Java debugger during runtime.
The fact that you have this difficulty is a symptom of a design problem. When something is hard to test, refactor until it isn't hard to test.
If one object needs to call too many methods of another, then encapsulation is poor, and responsibilities are poorly placed. Presumably, the Single Responsibility Principle is not being followed. If code calls methods that return objects, and must call methods on those in turn, then the Law of Demeter is not being followed.
Your pain comes from the fact, that your method does not comply with the Single Responsibility Principle. Your method does a lot of things with X -- and X also sounds a too comlex. This makes testing very hard -- even with mocking.
Break your method down into manageble chunks, that only do one thing each.
Related
I have a Unit Test in a Java app and I get error while executing the following line:
when(Arrays.asList(Locale.getISOCountries()).contains(countryCode)).thenReturn(true);
The error message does not mention mocking, but I think the problem is related to not mocking
Arrays.asList(Locale.getISOCountries() part in the when method. So, how should I treat this line? Normally I mock services and repositoories, but ı am not sure if I should mock this part like #Mock Locale locale;. I tried it but not worked.
Here is the error message:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Boolean cannot be returned by toString()
toString() should return String
Note: I also tried doReturn(true).when(Arrays.asList(Locale.getISOCountries()).contains(countryCode)); but not worked.
TL;DR: don't try to mock this. For one thing, you can't; for another, you shouldn't.
It is best to use the default behavior. Remove the line where you attempt to configure this behavior with Mockito.
If you insist that you need to be able to use an arbitrary list of countries, see the section on "dependency injection" below, noting the caveat mentioned.
Attempting to mock the return value of Arrays.asList(Locale.getISOCountries()).contains(countryCode) would specifically mean that you are trying to alter the behavior of the List<String> returned by Arrays.asList when given a particular String[] as an argument.
You can't with Mockito, because it isn't magic: it doesn't allow replacement of behavior for arbitrary expressions.
The way something like when(aMock.method(123)).thenReturn("hello") works is that the mock object - aMock - records that method was invoked with an argument 123. This information is pushed onto a stack, from which the when method is able to retrieve it and deal with it.
The pushing-onto-the-stack is only done if aMock is an object that Mockito has created: Mockito implements the methods of an interface/overrides the methods of a class to do this recording.
None of the objects involved in Arrays.asList(Locale.getISOCountries()).contains("GB") were created by Mockito. As such, none of these objects have methods which capture the invocation and push it onto the stack; Mockito basically can't see that anything is going on, so when the when call comes, it's just using whatever state is hanging around in Mockito. The error message shown:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Boolean cannot be returned by toString()
toString() should return String
indicates that it thinks you are configuring the behavior of a toString method, nothing obviously to do with Arrays.asList, Locale.getISOCountries() or List.contains.
For the same reason, you can't mock the return value of Arrays.asList(Locale.getISOCountries()) or Locale.getISOCountries(): they're just not objects Mockito knows anything about.
So, that's why this mocking doesn't work. On to why you don't want to do it in the first place:
List.contains has specific semantics, namely that it returns true if and only if the argument is in the list. There are implications of this, such as aList.contains(o) == true implying that a call to aList.indexOf(o) would have returned a non-negative value.
This means that the consequences of mocking contains would be either:
You have to also configure the mocking of other methods on the list such that the behavior of the list is consistent with that result of List.contains (so, indexOf, subList, iterator etc) - and, how the mock should behave if you were to set the element equal to o to something else (because Arrays.asList allows that);
or
You don't configure the other mocking, and your List doesn't have behavior consistent with an actual List, which will have unpredictable effects on the behavior of your code.
But you don't actually have to worry about doing 1. (which is good, because it would be essentially impossible to do correctly, e.g. such that copies of the list have the same contains behavior) or 2. (which is good, because introducing the unpredictability of a broken List is simply a bad idea): Arrays.asList has a perfectly-working implementation of the List interface; all you need to make sure is that the argument you pass in (in this case Locale.getISOCountries()) contains the element that you want.
Mocking of the return value of Arrays.asList(...).contains is neither necessary nor desirable.
So, the problem now is shifted from one of ensuring that Arrays.asList(Locale.getISOCountries()).contains(countryCode) to one of ensuring that Locale.getISOCountries() has at least one element that is equal to countryCode.
As stated in the Javadoc of Locale.getISOCountries():
Returns a list of all 2-letter country codes defined in ISO 3166
Given that GB (as was originally asked) is a 2-letter country code defined in ISO 3166, it will always (or, at least, until ISO3166 is changed) be an element of the array returned by Locale.getISOCountries().
Hence, the array returned by Locale.getISOCountries() is going to contain "GB", so Arrays.asList(Locale.getISOCountries()).contains("GB"), without actually doing anything.
But then there is the question of making Arrays.asList(Locale.getISOCountries()).contains(countryCode) true, for an arbitrary countryCode. By the argument above, you want to effect this only by ensuring that Locale.getISOCountries() has an element equal to countryCode.
If countryCode is another two-letter ISO-3166-2 country code, this already works, as in the GB case.
If countryCode is not an ISO country code, you absolutely should not want to make a method return it if that method is documented to return only ISO-3166-2 country codes, because this wouldn't happen in production code.
Mocking should not be used as a way to do arbitrary things in tests.
You only ever want a test to test things which can actually happen in production code. Ideally, you use "the real thing"; test doubles (of which a mock is one type) come into play only if using "the real thing" is hard because the real thing is slow, expensive, difficult to reproduce (e.g. an error condition like a network error or full disk) etc. But, the testing double should only be doing things you'd see for real.
So, even if you could mock Locale.getISOCountries() to ensure that it returned an array containing a non-ISO-3166-2 countryCode, you shouldn't, because this will never actually happen in production; and a test that tests something that cannot happen in production has very limited value in telling you something useful.
Actually, you can mock static methods like Locale.getISOCountries() using PowerMock; but changing the behavior of a static method is highly inadvisable, because it doesn't just change the behavior for you - it changes it for anybody who calls it. So, there could be unintended consequences in the behavior, both nearby and in the rest of the code.
For example:
when(Arrays.asList(Locale.getISOCountries())).thenReturn(Collections.singletonList(countryCode.toUpperCase(Locale.ENGLISH)));
Aside from changing the mutability semantics of the returned list (Arrays.asList allows set; Collections.singletonList doesn't), it is now inconsistent with other country-code-returning methods in the Locale class (e.g. getISOCountryCodes(Type). Chasing down and fixing all such inconsistencies is nigh-on impossible.
What if we could use PowerMock to mock the return value of Arrays.asList(Locale.getISOCountries()), i.e. a less generic use-case of the Locale.getISOCountries() method? This still suffers from the problems of unintended consequences - there could be other parts of the program which invoke Arrays.asList(Locale.getISOCountries()) where the mocked behavior isn't desirable.
What if we could use PowerMock to mock the return value of just one specific call to Arrays.asList(Locale.getISOCountries())? That's brittle, for example, if another call is added, you'd have to make sure the test is correctly updated, otherwise the behavior would be inconsistent between the calls.
There isn't a good way to win the PowerMock battle.
There are an awful lot of words here, but the key point is that there are really rather difficult-to-deal-with consequences of trying to use mocking inappropriately: using the actual behavior (without mocks) is best; but, if a mock has to be used, it should not behave in a way that the real code never will.
Fortunately, Mockito is stopping you from doing this; but hopefully this answer has given a thorough explanation as to why it was the wrong approach in the first place.
Dependency injection
With all of this said, there is a way to make your code work in the face of arbitrary country codes: dependency injection.
Whilst there are lots of DI frameworks (e.g. Guice, Spring) which introduce a lot of power (and complexity and horror), dependency injection simply means: passing things as arguments.
If, for example, the code in which you want Arrays.asList(Locale.getISOCountries()).contains(countryCode) to be true occurs in a method, inject the country list as a parameter to that method:
class MyClass {
void myMethod(List<String> countryCodes, String countryCode) {
if (countryCodes.contains(countryCode)) {
// ...
}
}
}
or make it a constructor parameter:
class MyClass {
private final List<String> countryCodes;
MyClass(List<String> countryCodes) {
// Defensive copy.
this.countryCodes = Collections.unmodifiableList(new ArrayList<>(countryCodes));
}
void myMethod(String countryCode) {
if (countryCodes.contains(countryCode)) {
// ...
}
}
}
In your production code, pass in Arrays.asList(Locale.getISOCountries()); in test code, pass in whatever list you like.
But still: beware of the interaction between this code and code which uses Locale.getISOCountries() and allied methods directly in tests. If there is a risk of such an interaction, it remains safer to write your tests using the static Local.getISOCountries().
I'm trying to write a unit test for an implementation of a Feign client. I wasn't sure how to go about it so I googled it and came across this answer and the accepted answer is this code snippet:
#Test
public someTestClient(){
Person expectedPerson = new Person("name",12));
when(mockPersonClient.getPerson()).return(expectedPerson);
Person person = mockPersionClient.getPerson();
assertEquals(expectedPerson, person);
}
I don't understand why this is a useful test or under what circumstance, other than a problem with the Person constructor, this test could ever fail. Isn't this test essentially the equivalent of:
Person person = new Person("a", 1)
Person expectedPerson = new Person("a", 1)
assertEquals(person, expectedPerson)
I understand unit testing should test functionality in isolation. Will this test just ensure that mockPersonClient exists at runtime?
We can configure a mock object to always return a hard coded and fake object when calling a method on that mock object.
In this example , the OP configured mockPersonClient.getPerson() return a fake Person.However, he just wonder why the fake person was not returned as configured when he called mockPersonClient.getPerson(). I think the codes example he shown was just for him to demonstrate this question. It does not mean he actually wrote that unit test codes to test some production codes.
A test like that doesn't have any value.
Here is a person, I am going to ask this call to return person and then I will check I got person when calling that thing. Of course you will get person, you just hard-coded that, so how is it useful?
Unit tests are about functionality, a simple fact which is lost on many.
Any code which changes data, which filters something, which changes something in a controlled way, is a good candidate for a unit test.
People use mocks a little bit too much and for most of the time for the wrong thing. Yes, we are advised to code against interfaces, but this doesn't mean you should have a very complex system, where you pass interfaces all over the place and then your test code tries to mimic that.
When you mock too much, it means the test you are writing is tied too much to the code it tests, it knows too much about it. Unit tests should not do that, because every time you change the code in some small way, you then discover that now you have to change the test, because you're no longer using 35 interfaces, now you have 47 to mock in a very specific order. That may not be an issue when you have one test, but imagine what happens when you have 1000 tests ...
If people tried to code in more of a functional way then this would not happen. If you pass data, instead of abstractions, now you don't have to mock anything.
Instead of mocking a call a database, isolate it, take the result and pass that to a method, you've just lost an abstraction and your code does not need to mock anything, you just call the method, pass the data in whatever format you want and see what happens.
If you want to test a real database, then you write an integration test. It's really not that complicated, mocking should not be the first thing you do, do it when it helps and you really must, but most of the time, you really don't and it simplifies things if you don't.
I have a class A that I want to test with JUnit. Creating of an object of type A involves a lot of IO, so it takes about 5 seconds.
A is mutable and I want to test the different methods that change A. Now I am in some kind of dilemma:
If I create a virgin object A for every test method, it just takes too long.
Creating one huge test method with a lot of asserts seems like a bad idea to isolate the causes of possible errors.
"Repairing" the object of type A after each test methods seems to dangerous as well because if the repairing is not done correctly, other test methods might fail without proper reason.
I could also create a deep copy of an instance of A for every test method, but that means that I have to change the class A only to properly test it.
What would you suggest?
I'm thinking about the solution for my application. Here's the situation: I have a class with a method that takes ObjectA as an input parameter and calls several small methods. Each one of these methods needs some parts of the ObjectA (they don't overlap, i.e. method1() needs ObjectA.field1 and ObjectA.field2, method2() needs ObjectA.field3 and so on...)
Given the general good code practices and performance, is it better to pass ObjectA to each one of these methods so they can extract the value they need on their own or is it better just pass them values? I mean:
method1(ObjectA);
method2(ObjectA);
or
method1(Object1.getField1(), ObjectA.getField2());
method2(ObjectA.getField3());
Keep in mind, with your code, you're not actually passing ObjectA. Namely, you're passing the reference type to ObjectA, so on a performance note the difference between passing a String object reference and a ObjectA object reference would be negligible.
The way I would write it
I would pass the whole object, if the method is pertinent to the class. My reasoning for this is to split up class knowledge as much as possible. What I mean by this is the following.
public void doSomethingRelatedToTheClass(String param)
{
// Do something with param.
}
My first criticism here is that this method assumes that the input is the correct field. My second, is that now, the class calling this code needs to know a little bit more about this method, because it has to call it like this:
doSomethingRelatedToTheClass(myObject.getValue());
And what this means is, if you find that another member of ObjectA works better inside this method, or you want to access other members of ObjectA, and you change doSomething() to reflect this change, you also need to change the method call, to:
doSomethingRelatedToTheClass(myObject.getOtherValue(), myObject.getValue());
So by passing in the whole object, you abstract that detail away, and the method can handle it; namely:
doSomethingRelatedToTheClass(myObject); // Doesn't need to know what you do with it.
public void doSomethingRelatedToTheClass(ObjectA object)
{
String val = object.getValue();
String otherVal = object.getOtherValue();
}
When a change to one class, results in a change in other classes, this is an Anti-pattern called Shotgun Surgery.
Edit
I've had chance to review my answer here and I've amended my original answer slightly because I believe it isn't the best solution for all situations. As above, if a method is related to a class specifically, then the instantiation of that class (or more preferably, its superclass or implemented interface[s]) should be the parameter.
The time this is not the case is when the functionality can be generic. An example of a generic function might be:
public String[] findNouns(String sentence);
In this case, finding the nouns in a sentence might be appropriate for lots of use cases, and not just the use cases that you have defined. As such, passing in the value is the only sensible approach because otherwise, you couple two pieces of logic together that have no direct relationship. The finding of nouns and the arbitrary object you have defined.
In Summary
If the method is logic that is related to the object, pass in the object
If the method has nothing to do with the object, and the object is just using it as a utility function, then pass in the value and name the function generically.
Let's examine a scenario. Now this may or may not be your scenario but it illustrates a point.
Lets say field1 and field2 in your case are two integers and method1 sums them and returns the result.
If you pass in the objects then that method can only ever sum those two fields. The method is also now strongly coupled with those objects.
On the other hand, if you pass in only the fields, the two integers in this case your method becomes more generic. You can now sum any 2 arbitrary integers regardless of which objects they are on.
In general though, always expose as little of your objects to other methods and classes. This promotes loose coupling.
Exceptions
AS maaartinus points out if for example field1 and field2 were Points and method1 calculated the distance between those two points, then I would have to agree that passing two Points would be better than passing 2 xy integer pairs (4 parameters)
Hope this helps
I'd say, it depends. A method may be clearer and more general if it operates on the arguments rather than requiring a whole object. Sometimes you have the arguments ready (e.g., x and y) and would have to aggregate them first into e.g. a Point in order to be able to call the method. Sometimes you have a different unrelated object (e.g., some ImmutablePoint, obviously not extending java.awt.Point) and would have to extract the coordinates and create an object to pass.
Usually, if the passed object is the proper abstraction, then passing it as a whole is the way to go. It's not a performance question, it's about readability and maintainability. See also the Law of Demeter which may lead to looser dependency on the passed object.
As others have said, it depends but in my experience passing entire objects makes code harder to read and maintain.
Lets consider this method getUserDetails(User user) which relies on few methods like getUserAddress(User user) getUserFamilyInfo(User user) etc which may further connect to different data sources to fetch the information.
There is no easy way to know that getUserFamilyInfo needs only userId or it needs userId and lastName or something else from user when entire object is passed. It makes it hard to understand dependencies among different services and do any refactoring.
I prefer to pass individual arguments if count is less than 3 or create a dto if handful of properties are required from a vary large object.
How can i get hold of the instantiating object from a constructor in java?
I want to store reference to the parent object for some GUI classes to simulate event bubbling - calling parents handlers - but i dont wanna change all the existing code.
Short answer: there isn't a way to do this in Java. (You can find out what class called you, but the long answer below applies there for the most part as well.)
Long answer: Code that magically behaves differently depending on where it's being invoked from is almost always a bad idea. It's confusing to whoever has to maintain your code, and it seriously hurts your ability to refactor. For example, suppose you realize that two of the places instantiating your object have basicaly the same logic, so you decide to factor out the common bits. Surprise! Now the code behaves differently because it's being instantiated from somewhere else. Just add the parameter and fix the callers. It'll save you time in the long run.
If you want to know the invoking class, then pass "this" as a parameter to the constructor.
Thing thing = new Thing(this);
Edit: A modern IDE allowing refactoring will make this very easy to do.
Intercepting method calls (including constructors) without changing a ton of existing code is one thing Aspect-oriented programming was made for.
Check out AspectJ for a start.
With AspectJ, you can define a "pointcut" that specifies that you want to intercept constructor calls for a certain object or set of objects (using wildcards if need be), and within the interception code ("advice"), you will be given method context, which includes information about the both the calling method and object.
You can even use AspectJ to add fields to your object's to store the parent reference without modifying their existing code (this is called "introduction").