I'm aware that there are many questions about mocking and testing, but I didn't find any that helped me perfectly, so I still have problems understanding the follwing:
Please correct me if I got this wrong, but as far as I see, unit tests are used to test the business logic of one particular class in isolation and if there are any objects needed from the outside they will be mocked.
So for example if I have a management system for citizens of a simple city that adds citizens to a list and returns the citizen by their names (Assumtion: citizens consist of only a few basic personal information), like this:
public class ProcessClass {
ArrayList<Citizen> citizenList = new ArrayList<Citizen>();
public void addCitizen(Citizen citizen) {
citizenList.add(citizen);
}
public Citizen getByName(String name) {
for (Citizen c : citizenList) {
if (c.getName().equals(name)) {
return c;
}
}
return null;
}
}
If now I want to unit test my ProcessClass do I consider Citizen as an external feature that has to be mocked, or do I simply just create a Citizen for test purposes?
If they are mocked, how would I test the method to get the object by its name, since the mock object is not containing the parameters?
As you're writing new code (along with the new unit tests) or refactoring existing code, you want to be able to run the unit tests over and over to be reasonably confident existing functionality was not broken. Therefore, the unit tests must be stable and fast.
Suppose the class to be tested depends on some external resource such as a database. You make a code change and the unit tests are suddenly failing. Did the unit tests break because of a bug you just introduced, or because the external resource is not available? There is no guarantee the external resource will always be available so the unit tests are unstable. Mock the external resource.
Also, connecting to an external resource can take too much time. When you eventually have thousands of tests which connect to various external resources, the milliseconds to connect to the external resource add up, which slows you down. Mock the external resource.
Now add a CI/CD pipeline. During the build the unit tests fail. Is the external resource down or did your code change break something? Perhaps the build server doesn't have access to an external resource? Mock the external resource.
To answer the first part of your question
If now I want to unit test my ProcessClass do I consider Citizen as an external feature that has to be mocked, or do I simply just create a Citizen for test purposes?
Without knowing more about Citizen it is hard to tell. However, the general rule is, that mocking should be done for a reason. Good reasons are:
You can not easily make the depended-on-component (DOC) behave as intended for your tests.
Does calling the DOC cause any non-derministic behaviour (date/time, randomness, network connections)?
The test setup is overly complex and/or maintenance intensive (like, need for external files)
The original DOC brings portability problems for your test code.
Does using the original DOC cause unnacceptably long build / execution times?
Has the DOC stability (maturity) issues that make the tests unreliable, or, worse, is the DOC not even available yet?
For example, you (typically) don't mock standard library math functions like sin or cos, because they don't have any of the abovementioned problems. In your case, you need to judge whether just using Citizen would cause any of the abovementioned problems. If so, it is very likely better to mock it, otherwise you better do not mock.
More often than not, mocking is used to replace actual calls that are hard to duplicate in testing. For instance, assume ProcessClass makes a REST call to retrieve Citizen information. For a simple unit test, it would be hard to duplicate this REST call. However, you can "mock" the RestTemplate and dictate the different types of returns to insure your code would handle the 200's, 403's, etc. Additionally you could change the type of information to then also test your code to insure that bad data is handled, like missing or null information.
In your case you can actually create a Citizen, and then test that the Citizen is an object in the list or that getByName returns the proper object. So mocking is not needed in this example.
In your particular example, no, you would not need to mock anything.
Let's focus on what you would test:
a test where you add and retrieve one Citizen
add 2 Citizens, retrieve one
pass null instead of citizen, and make sure your code doesn't break.
add two citizens with the same name, what would you expect to happen then?
add a citizen without a name.
add a citizen with a null name
etc etc
You can already see a number of different tests you can write.
To make it more interesting you could add some code to your class which exposes a read-only version of your citizenList, then you could check that your list contains exactly the right things.
So, in your scenario you don't need to mock anything as you don't have external dependencies on another system of some kind. Citizen seems to be a simple model class, nothing more.
If they are mocked, how would I test the method to get the object by its name, since the mock object is not containing the parameters?
You can mock the call to getName, using mockito for example:
Citizen citizen = mock(Citizen.class);
when(citizen.getName()).thenReturn("Bob");
Here is an example of a test for your method
ProcessClass processClass = new ProcessClass();
Citizen citizen1 = mock(Citizen.class);
Citizen citizen2 = mock(Citizen.class);
Citizen citizen3 = mock(Citizen.class);
#Test
public void getByName_shouldReturnCorrectCitizen_whenPresentInList() {
when(citizen1.getName()).thenReturn("Bob");
when(citizen2.getName()).thenReturn("Alice");
when(citizen3.getName()).thenReturn("John");
processClass.addCitizen(citizen1);
processClass.addCitizen(citizen2);
processClass.addCitizen(citizen3);
Assert.assertEquals(citizen2, processClass.getByName("Alice"));
}
#Test
public void getByName_shouldReturnNull_whenNotPresentInList() {
when(citizen1.getName()).thenReturn("Bob");
processClass.addCitizen(citizen1);
Assert.assertNull(processClass.getByName("Ben"));
}
Note:
I would recommend mocking. Let's say you write 100 tests where you instantiate a Citizen class this way
Citizen c = new Citizen();
and a few months later, your constructor changes to take an argument, which is an object itself, class City for example. Now you have to go back and change all these tests and write:
City city = new City("Paris");
Citizen c = new Citizen(city);
If you mocked Citizen to start with, you wouldn't need to.
Now, as it is POJO and its constructor of the getName method might not change, not mocking should still be ok.
Related
I don't know if I'm testing this method wrong or if the whole test is nonsense. (Example code is below)
I would like to test my ExampleService with Mockito. This has the client and the customerService as dependencies (both with #Mock). Both dependencies are used in exampleService.doYourThing().
client.findByName() returns a ResultPage, which in turn is used in customerService.createHitList(). I thought it would be a good idea to test that the page object does not change during the "doYourThing" method, after being returned by client.findByName(). Therefore, customerService.createHitList() should only return something if 'page' does not change.
The problem: If 'page' is returned in "doYourThing()" by client.findByName() and is changed with "page.setIsNew(true)" as in my example, then in my test method the 'pageForTest' object also changes. As a result, the condition I set with "given(this.customerService.createHitList(pageForTest))" is met and the customerList is returned.
I know that 'page' is a reference of 'pageForTest' and thus changes.
Anyway, the goal of my test is not fulfilled by this. How would you tackle the problem?
This is only sample code, there may be small syntax errors.
Test method:
ExampleService exampleService = new ExampleService(client, customerService);
ResultPage pageForTest = new ResultPage();
pageForTest.setIsFulltextSearch(true);
pageForTest.setIsNew(false);
pageForTest.setHits(hits);
given(this.client.findByName("Maria")).willReturn(pageForTest);
given(this.customerService.createHitList(pageForTest)).willReturn(customerList);
HitList hitList = this.exampleService.doYourThing("Maria");
ExampleService
public HitList doYourThing(String name) {
ResultPage page = this.clientFindByName(name);
page.setIsNew(true);
HitList hitList = this.customerService.createHitList(page);
return hitList;
}
When writing unit tests it's important to actually define the unit. A common misconception is treating every class as a separate unit, while we use classes to separate responsibilities, but a certain unit of work can be represented by more than one class (so it could include more than one responsibility as a whole). You can read more about it on the Internet, for example on the Martin Fowler's page.
Once you define a unit you want to test, you will need to make some assumptions regarding the dependencies used by the test. I will point you towards the Martin Fowler's page for more information, but in your case this could be a mock of the repository retrieving some data from the database or a service representing another unit, which has it's own tests and it's behavior can be assumed and reproduced using a mock. The important thing is that we do not say here that all the dependencies should be mocked, because maybe testing given method while mocking all the dependencies only tests Mockito and not actually your code.
Now it's time to actually understand what you want to test - what is the actual logic of the code. When developing using test-driven development, you write the tests first as the requirements are pre-defined and the tests can point you towards the best API that should be exposed by the class. When you have tests ready, you implement the code as designed during writing the tests. In your case you have your logic ready and let's assume it's doing what it's supposed to do - that's what you want to test. You are not actually testing the code, but the desired behavior of the code. After the test is written, you can change the code in many ways including extracting some parts to a method or even a separate class, but the behavior should be well defined and changing the internals of a method or the class (refactoring) should not require changing the tests, unless API or the expected behavior changes.
The main problem with trying to help you understand how to test your code is actually the lack of context. Method named doYourThing does not describe the expected behavior of the method, but this is actually the most important thing when writing a test - that thing should be done. If you strictly stick to how it's done internally in your test, the code will be hard to modify in the future and the tests may be unreliable. If the important behavior includes setting the isNew value, maybe the object returned from the mock should be a spy or it should be verified with assertions? If the actual logic lies in the customerService, then maybe it shouldn't be mocked, but it should be the part of the unit? It requires context, but hopefully I explained some things regarding testing as a concept. I recommend reading up on testing online as multiple articles like those by Martin Fowler can be more helpful in understanding it.
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 am wriring unit test cases for an existing system. The architecture for the underlying classes if very complex in itself.
Blockquote
RequestHanndler ==> processes ==> Order ===> is dependent on ==> service layer == connected to ==> DB layer.
I am writing a test case for RequestHandler. The method in test(doProcess()) creates a new instance of Order class. Order class itself has very tight dependency on the service layer. I want to create an atomic test case, so, not any other layer of code will be executed.
What should the best process to create test cases for these scenrios?
It might get a bit complicated when you want to write unit-tests for tighly coupled code. To make uni-testing easier you should better rely on abstractions and not on real implementations. E.g. the Order class shouldn't depend on the real implementation of the service layer, instead introduce an interface which is much easier to mock instead of a class which might be set to final.
Since your RequestHandler is responsible for creating the Order instances you'll have to provide a way to mock out the order class in unit-tests. A simply way is to create a protected method that simply creates a new order instance.
protected Order createOrder(String someParam) {
return new Order(someParam);
}
In your Unit-Tests you can now extend the class and overwrite the factory-method.
Using Mockito this would look like:
protected Order createOrder(String someParam) {
Order order = Mockito.mock(Order.class); // create mock object
// configure mock to return someParam when
// String Order#getSomeParam() gets invoked
Mockito.doReturn(someParam).when(order).getSomeParam();
return order;
}
Typical approach for unit testing of such systems is mocking. There are several mockup frameworks for java. I personally used EasyMock but there are others.
So, I think that you should to try to test the logic of request handler first. You should mock Order (i.e. create dummy, not real instance of order using mockup frameork). When this layer is tested go deeper and start testing internal layers.
Other strategy is going from down to up, i.e. test first the internal layers. This strategy is probably "right" but it you will not get fast results that you can show to your manager because managers typically like to see the "big" picture and very seldom go into the details.
Bottom line: good luck.
I have three classes I need to test, lets say Load, Transform, Perform and they all begin or work on the same data object, at least that's what is required, from one data object X the Load methods perform their thing on it, then it is given to Transform which also does its thing with its methods, and a Perform which changes the data object a bit and it is ready.
Now I want to write tests for Load, Transform and Perform.
The test-data object, should I just make a static method in the Load class like
public static TestData makeTestData(...makeit...)
OR should I make a TestDataMock or TestDataTest class ? Which can return an example of it? And make a new TestDataTest class in each Load, Transform and Perform when they need to work on it?
You should always strive to make unit tests independent of each other. For that reason, you should always create any input test-data fresh for each test, whenever possible. What you want to test is "given input data X, verify that output is Y". JUnit has the #Before annotation which you can use to annotate a method that is to be run before each test-case in that class. Typically, that is where you would put all your set-up code (creating and initilizing mock objects, creating or loading test-data, etc).
Alternativly, you could combine your Load, Transform and Perform actions into one test-case, but that would be more of an integration test than a unit test.
Sounds like a good example where dependencies would be useful, so you don't have to recreate the object every time (or worse, mock it). On top of that, you work with the real output produced by the previous phase, and you don't have to use statics (always a code smell).
JUnit doesn't support dependencies, but TestNG does:
#Test
public void load() { ... }
#Test(dependsOnMethods = "load")
public void transform() { ... }
#Test(dependsOnMethods = "transform")
public void perform() { ... }
If transform() fails, the final report will say "1 Passed (load), 1 Failed (transform) and 1 Skipped (perform)", and you know exactly where to look.
Most test-case classes should be in the style of testcase class per class: if your have a class X it has one corresponding class XTest. But that is not the only way of doing things; if you have a group of classes that cooperate you could use JUnit for some low-level integration testing of the cooperating classes. You need only think of a suitable name for this test-case class.
However, if you have a group of cooperating classes, consider hiding that fact behind a facade, or even just a single method call of some higher-level class. Then treat that facade or high-lelve method as something to unit-test.
Or, are you trying to say that you do not know how to test your three classes in isolation because they are very tightly coupled, and the behaviour of one can not be described without reference to the two others? That suggests you have a poor design: consider a redesign so you can describe the required behaviour of each class in isolation, and therefore test them (at least in part) in isolation.
IMO one of the main characteristics of a good TDD is: testing your class (or actually unit) in isolation.
When you do so, you are able to actually test single behavior in each test -- only one test will fall for a single problem you have.
For this you first must verify that there are no static references from your class (including constructors AKA new keyword).
in theory it is easy not to use any dependency injection framework and test your classes in complete isolation, for this you need to inject all dependencies in the constructor and create Factories classes that will call the new keyword.
I found this theoretical MO to be too hard to actually do in practice.
Am I missing something important in the process?
EDIT:
I do think that we should all aim to one failure for one code change. it will never be perfect but it will get your code closer to there, people forget that test code is also maintainable code, specification of tested code changes.
If you don't aim there you will end up deleting tests which is a poor solution
I don't think you need DI frameworks for unit testing - TDD has been around years before DI became a fashion. And in case of code which does not use an explicit DI framework itself, why would you use that for unit tests?
Sure, DI frameworks can simplify the task to some extent. OTOH they do it by putting the complexity elsewhere. My preference is to have self-containing unit tests, and so far I have almost always managed without a DI framework. That often requires refactoring my test code, and sometimes even duplicating code, but I can live with that. And, as #kostja noted, mocking frameworks are a big help too.
Designing for testability is important too - if a class is hard to test per se, it is better to refactor it rather than trying to alleviate the pain with a DI framework.
Note that all this requires lots of practice, as well as a change in mindset and ways of thinking. All of which takes time and patience... the more you keep up, the better you become :-)
you are able to actually test single behavior in each test -- only one test will fall for a single problem you have.
I think your conclusion is wrong here. Even if you test a single thing in each test, you can break a lot of tests with a single code change. Update: Every test is to verify a single path of execution within the tested code. However, those paths of execution are rarely independent - there tend to be common path sections in all but the simplest methods. Any change there can break multiple tests.
DI is supposed to reduce direct dependencies, actually simplifying mocking and unit testing. Maybe you are missing the mock part? this post could clarify things.
Sure, it is possible. And easy. For example, lets say you want to test the following method, which calls static methods on a Database class (a static persistence facade) and instantiates SimpleEmail (from Apache Commons Email) directly:
public void doBusinessOperationXyz(EntityX data) throws EmailException
{
List<EntityX> items =
Database.find("select x from EntityX x where x.xyz=?", data.getXyz());
BigDecimal total = ...compute/obtain the value...
data.setTotal(total);
Database.persist(data);
Email email = new SimpleEmail();
email.setSubject("Notification about processing of ...");
email.addTo(data.getCustomerEmail());
email.setMsg("...some notification message...");
email.send();
}
This method can be tested in isolation from the Database and Email dependencies with the following unit test, using JMockit:
#Test
public void doBusinessOperationXyz() throws Exception
{
final EntityX data = new EntityX(5, "abc", "5453-1");
final List<EntityX> items = new ArrayList();
items.add(new EntityX(1, "AX5", "someone#somewhere.com"));
new Expectations()
{
#Mocked final Database unused = null;
#NonStrict SimpleEmail email;
{
Database.find(withSubstring("select"), (Object[]) any); result = items;
Database.persist(data);
email.send(); times = 1;
}
};
new MyBusinessService().doBusinessOperationXyz(data);
}
Normally, it would be necessary to inject mock objects for the Database and Email dependencies, forcing a more complex solution. By mocking/stubbing static methods and constructors, we can keep a simple, natural, and elegant design instead.