I never had to stub a call that accepts a functional interface/method reference as argument, so I just find out about the obvious incapacity for compare with "equals" these, aside from the reference comparison.
So how does one deal with this scenario during testing? Do we have to use any() either we want it or not?
Comparator<String> comparator = Comparator.naturalOrder();
when(myColaborator.isCalledWith(comparator)).thenReturn("foo"); //This is not gonna work as long as the real call does not use theat very same reference
It is necessary to verify its collaborator whether has recieved an exactly Comparator in a simple test case since such a communication protocol test in a book of GOOS describes whether they will work together correctly. and then skip checking the rest of tests by using any(Comparator.class). you can read it further in Martin's blog:
in the second test case is that I've relaxed the constraints on the expectation by using withAnyArguments. The reason for this is that the first test checks that the number is passed to the warehouse, so the second test need not repeat that element of the test. If the logic of the order needs to be changed later, then only one test will fail, easing the effort of migrating the tests. As it turns out I could have left withAnyArguments out entirely, as that is the default.
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 have a Java class with three fields. I realized I only need two of them due to changes in requirements.
Ideally I'd write a failing test case before modifying code.
Is there a standard way, or should I just ignore TDD for this task?
That's refactoring, so you don't need to start with failing tests.
Find all the methods using the field.
Make sure that they're covered by unit tests.
Refactor the methods so they no longer use the field.
Remove the field.
Ensure that the tests are running.
Does the drop of this field change the behavior of the class? If not, just drop the field and check if the class still works correctly (aka, passes the tests you should have already written).
TDD principle is to write code "designed by tests". Which may be sound silly but means that the first class you should write is the test class, testing the behavior of the class under test. You should iterate over few steps:
write the test. It should not compile (you don't have the class/classes under test)
Make the test compile. It should fail (you just have empty class which does not satisfy the assertions in the test)
Make the test to pass in the simplest way (usually, just making the method you are testing to return the expected value)
Refine/Refactor/Generalize the class under test, re-run the test (it should still pass). This step should be really fast, usually less than 2 minutes.
Repeat from step 2 until the desired behavior will emerge almost naturally.
If you have an exhaustive list of all the fields you need, you can compare that list of fields by reflection :
yourClassName.getClass().getDeclaredFields() vs your list of fields
Write a test for the constructor without the field you want to remove.
Obviously only works if the constructor takes the field's value as a parameter.
Delete all tests covering the removed functionality (this doesn't count as "writing production code" as per the 3 Rules of TDD).
Delete all references to the obsolete field in remaining tests. If any of them is to fail, you are then allowed to write the required production code to make it pass.
Once your tests are green again, all subsequent modifications fall into the "refactoring" category. You are allowed to remove your (now unused) field here.
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.
I am using EasyMock to unit test my Java code. The class I'm trying to test is a RESTful webservice API layer. The API has an underlying service layer which is being mocked in the API test. My problem is figuring out how to correctly unit test my editObject(ID, params...) API method, since it calls service.getById() twice and expects a different object to be returned with each call.
editObject(ID, params...) first tries to grab the object from the service layer to make sure the ID is valid (first service.getById(ID) call to expect, returns original unmodified object). Next it modifies the parameters specified in the API call, saves it to the service, and calls get again to hand the caller the service-managed modified object (second service.getbyId(ID) call to expect, returns modified object).
Is there a way to represent this with EasyMock?.
Sure, you can do two different things for two method calls with the same method and parameters. Just declare your expectations in the order you expect them to happen and set up the responses accordingly.
expect(mockService.getById(7)).andReturn(originalObject).once();
expect(mockService.getById(7)).andReturn(modifiedObject).once();
replay(mockService);
The .once() is optional but I find in this case that it's more self-documenting.
You can chain multiple andReturn method calls:
EasyMock.expect(service.getById(1))
.andReturn(firstObject)
.andReturn(secondObject);
The first time service.getById is called with 1 as argument the mock will return firstObject and the second time secondObject. You can chain as many as you want and even throw an exception via andThrow for a specific call.
This technique is also helpful in conditional expressions in which you may want to invalidate the first condition but pass the second one or vice-a-versa.
If I have a basic accessor method that returns an ArrayList
What exactly would i test for it?
I am very inexperienced when it comes to testing.
That depends on how you expect the method to behave. For example: If someone has called the method and changed the list that was retrieved, do you want those changes to show up the next time the getter is called? Either way, test that behaviour. What does the getter return when the list would be empty? Null or an empty list? This should also be tested.
Typically writing explicit Junit tests for accessors is usually a little overkill (what are you testing? return foo;). Using a code coverage tool such as clover can help you target your testing efforts at your most complicated code first.
Interessting question for you:
How much should a unit test "test"
How to use Junit and Hibernate usefully
What should not be unit tested
Edit:
Added some of my favorite Questions here on Stackoverflow regarding JUnit and Unit Testing.
Always keeps in mind that test code is also code and for every 1,000 lines of code, you produce at least 4 bugs. So test what doesn't work and don't write tests for something that can't possibly break (like code generated by your IDE). If it does break, write a test :)
In general unit tests should test that your method does what it states it should do.
If your method returns an arraylist your basic test is to assert that an arraylist is indeed returned when it is called.
The next level of detail in the test is to check is the arraylist constructed correctly? Have the values you expect in it been filled correctly? If it's supposed to be an empty list, is that the case?
Now you have your "sunny day" case (i.e the method works under normal conditions) you should add some negative (or "rainy day") conditions if appropriate. If the method accepts a length for the array, what if you pass in a negative number or a int.Max etc.
As stated in another answer, this is probably overkill for a simple accessor, but the principles apply to any unit tests you need to write.
Depends on your requirements. You might test:
If the return value is not null
If the returned collection is not empty
If the returned collection is modifiable/unmodifiable
If the returned collection is sorted
If the returned collection contains all expected values
If the accessor method does not throw a runtime exception
But, as I said, it depends on the requirements, it depends on the 'kind' of collection you expect when you call the accessor. Maybe you allow setting the list to null but create an empty list. The the test could make sure that you really get an empty list when you set the list to null.
Hope it helps to give you an idea!