How to test a method that depends on another already tested method? - java

I'm working on a method that can be considered a specialization of another already defined and tested method. Here's an example code to illustrate:
public class ProductService {
public void addProduct(Product product) {
//do something
}
public void addSpecialProduct(Product product) {
addProduct(product);
//do something after
}
}
I don't want to copy the tests I have for addProduct which are already pretty complex. What I want is when I define the tests for addSpecialProduct, I just make sure that it also calls addProduct in the process. If this were a matter of 2 classes collaborating, it's easy to have the collaborator mocked and just verify that the target method gets called (and stub it if necessary). However, the 2 methods belong to the same class.
What I'm thinking right now is to spy on the object I'm testing, something like:
public void testAddSpecialProduct() {
//set up code
ProductService service = spy(new DefaultProductService());
service.addSpecialProduct(specialProduct);
verify(service).addProduct(specialProduct);
//more tests
}
However, I'm wondering whether this approach somehow defeats the purpose of unit testing. What's the general consensus on this matter?

I think it depends on how rigorous you want to be with your unit testing. In the extreme sense, unit testing should only test the behavior, not the implementation. This would mean you would need to duplicate your tests (or take take #chrylis' suggestion of abstracting out common functionality to helpers). Ensuring the other method is called is testing the implementation.
However in reality, I think your idea of spying on the instance to ensure the other well-tested method is called is a good idea. Here are my reasons:
1) It simplifies your code.
2) It becomes immediately clear to me what is happening. It says to me that everything that has been tested for the other method will now also be true, and here are the extra assertions.
One day you may modify the implementation so that the other method is not called, and this will cause your unit tests to fail, which is what people are generally trying to avoid by not testing the implementation. But in my experience, changes like this are much more common when the behavior is going to change anyway.

You may consider refactoring your code. Use the strategy pattern to to actually implement the functionality for adding products and special products.
public class ProductService {
#Resource
private AddProductStrategy normalAddProductStrategy;
#Resource
private AddProductStrategy addSpecialProductStrategy;
public void addProduct(Product product) {
normalAddProductStrategy.addProduct(product);
}
public void addSpecialProduct(Product product) {
addSpecialProductStrategy.addProduct(product);
}
}
There will be 2 Implementations of the AddProductStrategy. One does that what happend in your original ProductService.addProduct implementation. The second implementation will delegate to the first one and then do the additional work required. Therefore you can test each strategy separately. The second strategy implementation is then just a decorator for the first implementation.

Related

Mockito a void method

How do I write a mockito test for the below method? IntReqDecorate.decorate adds an Id to a call.
public class IntVisitor implements Visitor {
private final IntReqDecorator intReqDecorator;
public InternalCallVisitor() {
this.intReqDecorator = new IntReqDecorator();
}
#Override
public void apply(Call call) {
intReqDecorator.decorate(call);
}
}
You're in a bit of a bind here. Your IntVisitor class is very tightly coupled to the concrete class IntReqDecorator. And the apply method is defined, verbatim, to do the same thing as intReqDecorator.decorate. So without changing any of the signatures, the best you can possibly do is write the same test you did for decorate but over again.
Now, what you probably should do with this code is break that dependency. First, your constructor concretely builds an IntReqDecorator the moment it's constructed. You can still do that as a handy default, but you should provide a way for the caller to specify the decorator they wish to use. We can do that by overloading the constructor.
public InternalCallVisitor() {
this(new IntReqDecorator());
}
public InternalCallVisitor(IntReqDecorator intReqDecorator) {
this.intReqDecorator = intReqDecorator;
}
Now this alone is enough firepower for us to write a good test. We can mock IntReqDecorator and use the one-argument constructor in tests.
But I would go even further. You only ever use one method from IntReqDecorator, namely decorate. But since it's a concrete class, it probably has other methods that we don't really need here. So in an effort to follow dependency inversion, it may be a good idea to create an interface IntReqDecoratorLike (choose a better name for your use case) that has just that one method, and then have IntReqDecorator implement that interface.
Then your constructor takes a IntReqDecoratorLike that is capable of doing only exactly what we need it to. The great thing about this is that you barely even have to mock anything to test it. You could theoretically just write a new (ordinary) class that implements IntReqDecoratorLike and use that in tests. We'll probably still use the mocking framework, since it does provide good error messages and built-in validation, but the alternative is there in principle.
As a very broad general rule, when you find yourself scratching your head and saying "This code looks difficult to test", you should take a step back. Because oftentimes, you can make a change to the API that not only makes testing easier but also makes the code more ergonomic to use down the road.

JUnit test methods can't return a value

Why JUnit test methods can't return a value?
Documentation says (emphasis mine):
Test methods and lifecycle methods may be declared locally within the current test class, inherited from superclasses, or inherited from interfaces (see Test Interfaces and Default Methods). In addition, test methods and lifecycle methods must not be abstract and must not return a value.
Why is it enforced like this?
Is this purely a design choice? Is it a best practice?
One thing I haven't yet seen mentioned is that one of the reasons behind this design decision is historical.
Before test cases where marked by annotations, they were analysed by JUnit via reflection - all public void methods, the name of which started with test, and that didn't have any arguments, were considered test cases that were run by the framework.
Had these methods allowed to also return a value, you could not have created a public helper method starting with test that calculated a value commonly used in some tests, or the framework runner would have tried to run it.
You should have made them private anyway I guess.
Additionally, this was also the high time of "a method name that starts with a verb should do something" (rather than calculate something).
What is the reasoning behind this?
Think of each test method as being the logical analog of a public static void main(String[]) method in a classic Java application. There needs to be a well known method signature so that the JUnit framework is able to identify the test methods, and call them in a standard way.
As to the specific design points you are querying:
The reason that the methods cannot be abstract is that they need to be callable. You can't call an abstract method.
The reason that methods cannot return values is that the test framework would not be able to do anything useful with a returned value. (It would not be impossible to discard any result, but what is the point of a testcase result that can't be used?)
I was thinking maybe we could reuse one test method within another.
If you wanted to reuse code in unit tests, you would just use ordinary methods and other OO features of Java; e.g. base classes, helper classes, etcetera. There are few situations where you would want a free-standing test method and a "component" (for want of a better word) of another test method. And if you did you could write something like this.
public void test_xxx() {
real_test_xxx();
}
public int real_test_xxx() {
...
}
public void test_yyy() {
...
res = real_test_xxx()
...
}
Is this purely a design choice? Is it a best practice?
There are no best practices!
These are design choices that are guided by the general requirements and practicalities of unit testing1. Particularly, the requirement that unit test authors should NOT need to build configurations to tell the test framework what to do.
Note that TestNG takes a similar approach. If a TestNG #Test method is declared as returning a value, it is not treated as a test method; see Can a test method return a value in TestNG
Either way, unless you are proposing to implement yet another Java test framework, this is all moot.
1 - If there were strong practical reasons for needing abstract test methods or test methods that returned values, someone would have done something about it. The masses (of unit test writers) would rise up and overthrow the tyranical regime (of test framework developers). (Reprise of the "We won't be fooled again" by The Who.)
Is this purely a design choice?
Yes. In theory, nothing prevents the Junit runner (component thats runs tests of a test class) from accepting as valid a test method with a return value.
Is it a best practice?
Indeed. More you restrict the ways of specifying a unit test method, more you ensure that no misuse happens.
For example look at that simple example that would suppose a test method to return a value.
A addFoo() test method that asserts that the method does what it is expected and that also returns the Foo fixture created :
#Test
public Foo addFoo(){
Foo foo = new Foo(...);
// call the api ...
sut.addFoo(foo);
// assertion
assertEquals(...);
// return the foo fixture
return foo;
}
And here a addFooBar() test method that retrieves the Foo fixture created by the previous method and then asserts that the method addFooBar() does what it is expected :
#Test
public void addFooBar(){
Foo foo = addFoo();
Bar bar = new Bar(..., foo);
// call the api ...
sut.addFooBar(bar);
// assertion
assertEquals(...);
}
That lays multiple issues :
Does the method addFoo() should be executed multiple times ? It may hurt the test speed execution and make test reports unclear.
The addFoo() test result may make the addFooBar() test result to fail in spit of a correct implementation.
How to diagnostic the issue origin ? We added useless complexity.
The addFoo() test result may make the addFooBar() test result to succeed in spit of a incorrect implementation. Indeed, an incorrect implementation in addFoo() may compensate an incorrect implementation in addFooBar()
Test methods should be executed independently
If the tests can not run independently then they are not unit tests.
A unit test should not rely on any external state
If test method will return a value, it may applies that test results should be used/relevant to other tests
Same best practice in TestNG
Since TestNG follows best practices for unit testing, which means a unit test method should not have a return value
you can write some private methods and reuse them like
#Test
public void test1() {
privateTest();
}
#Test
public void test2() {
privateTest();
// more Logic
}
private int privateTest() {
return 0;
}

Test a class's method(s) in different test classes

I have an orchestrator class which has one method implements a feature via calling multiple methods:
public class Orchestrator {
public void doImportantStuff(){
firstDoThis();
thenDoThis();
finallyDoThis();
}
private void firstDoThis(){
...
}
private void thenDoThis(){
...
}
private void finallyDoThis(){
...
}
}
My question is, I have lots of cases to test for all methods called from doImportantStuff() so I'm planning to write separate test classes for each method. Is it something I should avoid, or does it sounds good?
This actually depends on which kind of tests you are about to write. Unit-testing assumes you test each method in isolation, but integration test will test all-in-one.
Assuming the methods do contain some business logic I'd recomment to write a set of unit tests for each of the method (maybe moving them into separate classes to comply the Single Responsibility principle).
After you are sure that all the methods works fine, it is time to write an integration test and test how the logic integrates (e.g. the particular call order). BTW, here could be useful some mocking tools as you are not going to test each method again.

Is it bad practice to make a method package or private for the sake of testing?

In Java (in an Android specific context, but this should apply across the board), is it considered bad practice to remove a private modifier - and thus be package specific - for the sake of unit testing?
Say I have something like the following:
public void init(long id) {
mId = id;
loadItems(1);
}
public void refresh() {
loadItems(mId);
}
private void loadItems(int page) {
// ... do stuff
}
In this case, I have 2 public methods that should definitely be tested. The concern is that both the refresh() and init() methods are almost identical minus some logic around handling the id.
It seems like it'd be easiest to write a unit test for loadItems() and then just verify that both init() and refresh() call loadItems() with the appropriate id (using something like Mockito). There's not a "good way" to test private methods though.
Would doing that make me a bad software developer? I know private methods technically shouldn't need unit tests, but this would be a straightforward testing approach, IMO, especially if loadItems() is somewhat complex.
You aksed "Would doing that make me a bad software developer?".
I don't think it makes you a bad developer. If you look at .NET for example, they even have a way to allow other libraries to see the internals of another library for the purpose of unit testing (InternalsVisibleTo).
I am personally against testing private methods though. In my opinion unit testing should be done on visible methods and not private methods. Testing private methods kind of defeats the point of encapsulation and making a method more visible than it needs to be just for the sake of unit testing looks wrong to me.
If I were you, I would instead test both my public methods. Today, your two methods are almost identical and it's easier to test your private method by making it package visible. Tomorrow however, that may no longer be the case. As both methods are public and easily accessible to other classes, you may be testing the wrong thing if such a scenario happens and the two drift apart.
Better yet (and this is what I would recommend) is to move
private void loadItems(int page) {
// ... do stuff
}
to its own class with its own interface and then test loadItems(int page) once using a separate unit test and then test the two public methods by just making sure they call the interface with the arguments you're expecting. This way, you're testing your entire code and avoid the pitfall I explained above.
IMHO it is better to have tests than not to, if it makes the code better and easier to maintain I think its a good idea.
I also agree with Niek above re putting the logic in another class.
I would also add that the method is void, and so has side effects which I think are harder to test than simply asserting a returned value.
Perhaps consider something like
List loadItems(int page)
Then check the list returned.

Mock Domain Entities in Unit Tests

I have a pretty simple question: I've been writing some Unit tests to a command object which has a Context object. This context has some domain entities inside of it.
public class Context {
private DomainEntity domainEntity1;
private Dto dto1;
// getters and setters go here...
public boolean isDomainEntityValid() {
// a little bit of logic goes here
}
}
public class Command {
public void execute(Context context) {
// do its logic in here
}
}
The DTO and the DomainEntity have nothing but setters and getters and very simple validation methods (such as isFirstNameValid()).
The Context object does have logic in it - after all, it checks if the context is consistent, is the context is complete, and so on.
When unit testing the command object, it's pretty clear to me that the context should be mocked out - but what about the entity and dto? Should I mock them? If so, I'll have to do a lot of code like the one below
doReturn(1L).when(domainEntity1).getId();
doReturn("phil").when(domainEntity1).getName();
In the another words, a lot of behaviour for the getters methods will have to be defined.
So, bottom line: should I mock Domain Entities and DTOs when unit testing an object?
I think you're probably violating the "Law" of Demeter here. I put that in quotes because you shouldn't follow this as a law but as advice.
You're not really giving us enough context to be able to tell you what you should specifically change (ie: why does the command need the id and the name?), but there's this other principle called Tell, Don't Ask and I think if you change your code to follow that, your code will become much easier to test.
(I think I may elaborate a bit more on my comment)
Whether you need to do the mocking etc all depends on what's the logic in your System-Under-Test (SUT, in this case, your Command).
Whole idea of mocking/stubbing is we don't what our testing to SUT depends on other actual code. Therefore we make up the mocks/stubs which fit for use in test, so the validity of test only depends on SUT, but not other actual code (of course, given that your mock/stub is correctly written, but that's normally not a problem due to its simplicity)
So, if your logic of Command is something like
DomainEntity domain = context.getDomainEntity();
domain.doSomething();
then yes, you need to do the mocking for your domain entity.
However, if you are simply working against the context, like:
if (context.isDomainEntityValid()) {
doSomething();
} else {
doAnotherThing();
}
then there is no point to mock the domain entity.
One more thing to note, with help of mocking framework, you can simply do stubbing according to your SUT logic. You don't need to do stubbing for EVERY method.
Therefore, if your Command is only calling domain.doSomething(), just stub this method. Forget about DomainEntity#anotherMethod() DomainEntity#getId().

Categories