I have the following method and I wrote a unit test in Java for this method. It is coveraged except from the if statement and I also need to test this part.
#InjectMocks
private ProductServiceImpl productService;
public void demoMethod(final List<UUID> productUuidList) {
if (productUuidList.isEmpty()) {
return;
}
final Map<ProductRequest, PriceOverride> requestMap = getPriceRequests(uuidList);
productService.updateByPriceList(priceRequestMap, companyUuid);
}
However, as the method execution is finalized and does not return anything when uuidList is empty, I cannot test this if block.
So:
How can I test this if block?
Should I create a new Unit Test method for testing this if block? Or should I add related assert lines to the current test method?
Update: Here is my test method:
#Test
public void testDemoMethod() {
final UUID uuid = UUID.randomUUID();
final List<Price> priceList = new ArrayList<>();
final Price price = new Price();
price.setUuid(uuid);
priceList.add(price);
productService.demoMethod(Collections.singletonList(uuid));
}
The general idea is that you don't want to test specific code, but you want to test some behaviour.
So in your case you want to verify that getPriceRequests and priceService.updateByPriceList are not called when passing in an empty List.
How exactly you do that depends on what tools you have available. The easiest way is if you already mock priceService: then just instruct your mocking liberary/framework to verify that updateByPriceList is never called.
The point of doing a return in your if condition is that the rest of the code is not executed. I.e., if this // code omitted for brevity was to be executed, the method would not fill it's purpose. Therefore, just make sure that whatever that code does, it was not done if your list is empty.
You have 3 choices:
Write a unit test with mocks. Mockito allows you to verify() whether some method was invoked.
Write a more high-level test with database. When testing Service Facade Layer this is usually a wiser choice. In this case you can obtain the resulting state of DB in your test to check whether it did what it had to.
Refactor your code to work differently
Check out Test Pyramid and How anemic architecture spoils your tests for more details.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have no test experience and try to test a method by a Unit Test. All the examples that I have a look at perform operations via uses mock values. I know, I will also use mock values with mockito that I use in my project. Here is my service method that I want to test:
ProductServiceImpl:
public List<ProductDTO> findAllByCategoryUuid(UUID categoryUuid) {
// code omitted
return result;
}
Here is my Unit Test class:
ProductServiceImplTest:
// ? #InjectMocks
#Autowired
ProductServiceImpl productService;
#Mock
ProductRepository productRepository;
#Test
public void testFindAllByCategoryUuid() {
UUID categoryUuid = UUID.randomUUID();
final List<Product> productList = new ArrayList<>();
for (int i = 0; i < size; i++) {
// create product by setting "categoryUuid" and add to productList
}
productRepository.saveAll(productList);
when(productService.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);
}
My questions:
1. Is the approach above is correct in order to test the service method? I think I should not deal with inside the service method and just pass categoryUuid and check the result of that method for testing? Is that true?
2. In test class, I used #Autowired to access service method, but I am not sure if I should #Mock. Is there any mistake?
Any help would be appreciated.
Update: I also create unit test using DiffBlue plugin and it generates a test method as shown below. But I think it seems to be as testing repository methods rather than the service method. Is not it?
#Test
public void testFindAllByCategoryUuid() {
when(this.productRepository.findAllByCategoryUuid((UUID) any()))
.thenReturn(new ArrayList<Product>());
assertTrue(this.productService.findAllByCategoryUuid(UUID.randomUUID())
.isEmpty());
verify(this.productRepository).findAllByCategoryUuid((UUID) any());
// ...
}
I am not an expert but I will try to answer you question
The general approach to unit test your method should be to test the output against all possible set of inputs.
in your specific case you can test
input: existing UUID output : NonNull List.
input: non existing UUID output : Empty List.
input: null : Empty List.
Now what you have done here is right you need to Autowire the class that you are writing test cases for and mock the dependencies in that class.
Only thing wrong is
when(productService.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);
should be
when(productRepository.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);
here you are mocking productRepository.findAllByCategoryUuid as your goal is to test the method in service class.
after this just add appropriate assert statements for all the conditions mentioned above.
Also I usually follow a rule whenever bug is logged against some code I try to cover that input and output case using assert in my Junit so that every-time I will test all the possible input and output scenarios.
The important things to remember while writing Junit Tests using Mockito
All class level #Runwith()
Test class should be with #InjectMocks
All tests should be annotated with #Test
Any external service should be Mocked with #Mock
Any calls going to DB or other services should be mocked and values should be returned accordingly.
You should have assertions to test your result.
I would write something like this :
#RunWith(MockitoJUnitRunner.class)
public class ProductServiceImplTest {
#InjectMocks
ProductServiceImpl productService;
#Mock
ProductRepository productRepository;
#Test
public void testFindAllByCategoryUuid() {
UUID categoryUuid = UUID.randomUUID();
final List<Product> productList = new ArrayList<>();
for (int i = 0; i < size; i++) {
// create product by setting "categoryUuid" and add to productList
}
when(productRepository.saveAll(ArgumentMatchers.any()).thenReturn(productList);
// Or below might work for newer version of test cases when we get Null Pointer Exp using older version of Junit test cases
//doReturn(productList).when(productRepository).saveAll(any(List.class));
List<ProductDTO> response = productService.findAllByCategoryUuid(categoryUuid);
Assert.assertNotNull(response);
Assert.assertEquals("Object Value", response.getXXX());
}
Writing unit tests against Service layer comes with drawbacks:
You violate encapsulation of the method-under-test. If it changes because you start to invoke different classes/methods - the test will break. Even though the method may be working correctly.
Because you intend to use mocking, partly your tests will be simply checking that your mocks are set up to pass the tests. So basically you'll be testing test logic.
It's usually more productive to move the logic down e.g. to the Model. Those classes could be then unit-tested without mocks. Then you could write a higher-level test (including DB) that checks that everything works together.
Reading:
Anemic architecture - enemy of testing
Building Test Pyramid to optimize automated testing
I want to test that a specific method produces the expected result, but to do that I need to manipulate the input in the test as well.
class ToTest {
public String produceResponse(String input) {
// ....
encryptedIds = encryptIds(input)
output = doStuff(input, encryptedIds)
}
public encryptIds(input) {
....
}
}
In my test I need to check that produceResponse actually produces the expected response.
in order to do that I have to encrypt the ids in the input.
My question is: should I rewrite encryptIds in the test (so that I would have more controller on the result) or should I call encryptIds from the class itself.
Is there a better approach to solve this? I don't like that in my test I know what happens in the specific flow.
If I understand correctly, you would like to test produceResponse() with known encryptedIds as input.
You could do that without refactoring the code, but it would probably be a good idea to refactor it, so that's what I'm going to explain:
class ToTest {
private IdEncryptor encryptor;
public ToTest(IdEncryptor encryptor) {
this.encryptor = encryptor;
}
public String produceResponse(String input) {
String[] encryptedIds = encryptor.encryptIds(input);
return doStuff(input, encryptedIds);
}
}
Now you can unit-test IdEncryptor to test that it produces correct encrypted IDs based on a String input.
And to test the ToTest class, you can mock the IdEncryptor so that whatever the input it receives, it produces the encryptedIds you desire. For example with mockito:
IdEncryptor mockEncryptor = mock(IdEncryptor.class);
when(mockEncryptor.encryptIds(any(String.class)).thenReturn(new String[] {"a", "b"});
ToTest toTest = new ToTest(mockEncryptor);
String response = toTest.produceResponse("input");
// expect that the response is what you expect given "a", "b" as input of doStuff()
Never copy any production code into the unit test as it will get outdated at some point.
If both methods are public, they are part of the public API, so:
you should first unit test the correct behavior of the encryptIds(String) method
then unit test the produceResponse(String) method which will internally use the already tested encryptIds(String) method
If encryptIds(String) would not be part of the public API:
then it is internal implementation and helper method which is not unit testable
produceResponse(String) is then responsible for encryption as a side-effect:
you can still test it if you mark it package private (no modifier)
you can also change the implementation of the encryptIds(String) only for testing purposes
Is encrypting id's something that is integral to your system or not? As it stands this class takes some input and produces some output and as far as your test is concerned this is what's important, no more, no less.
What is the impact of not performing the encryption? If your doStuff method will just fail if it doesn't happen then it is an internal detail to your class-under-test and I wouldn't have the tests care about it at all. If it's a step that absolutely must be performed then I would refactor the code to verify that it absolutely has happened, maybe using a mock as #jb-nizet answered.
As for the general case of duplicating production code in tests, as #Crazyjavahacking stated you should not do this, but I have no issue with using production code from a test- maybe not at a unit level but definitely the higher up the system I go, e.g. when testing writing to a DB I will use the reading code to verify it's happened correctly, but will also have independent tests to verify the reading path as well
How can I test the following code?
class1 {
public InjectedClass injectedClass;
method1(){
returnValue = injectedClass.someMethod;
//another logic
}
method2(){
resultValue = method1();
}
}
My application was developed in Java. I use JUnit and Mockito.
To test method1() I can create a mock for InjectedClass and a mock logic for someMethod().
But how does one properly test a method? Do I need to create a mock for method1()?
UPDATE:
Let me demonstrate real example.
public class Application {
#Inject
DAOFacade facade;
//method1
public ReturnDTO getDTO(LiveServiceRequestParam requestParam) throws AffiliateIdentityException {
ReturnDTO returnDTO = new ReturnDTO();
CoreProductRepository repo = recognizeProduct(ProdCodeTypeEnum.MPN, null, vendorBound);
if(repo!=null){
//logic to fill some fileds in returnDTO
}
return returnDTO ;
}
//метод2
CoreProductRepository recognizeProduct(ProdCodeTypeEnum paramType, String prodCode, List<Integer> vendors) {
CoreProductRepository coreProductRepository = null;
switch (paramType) {
case MPN:
coreProductRepository = facade.findByAlternativeMPN(prodCode, vendors);
break;
case EAN:
coreProductRepository = facade.findByEan(prodCode, vendors);
break;
case DESCRIPTION:
coreProductRepository = facade.findByName(prodCode, vendors);
break;
}
return coreProductRepository;
}
}
So, to test recognizeProduct i mock DAOfacade. But also I want test getDTO method which uses recognizeProduct method.
You don't need to mock out your recognizeProduct method. As long as the DAOfacade is mocked, the behavior is known and deterministic, so the results of both getDTO and recognizeProduct can be verified.
It can also be argued, that you don't even need to test recognizeProduct specifically, because it is not public, so, there is no contract to enforce. As long as the behavior of getDTO is being tested and verified, your API is working as far as the user is concerned. The details of implementation aren't important.
In a way, testing recognizeProduct specifically is counter-productive, it hurts the maintainability and reliability of your code rather than helping it, because it makes any refactoring or reorganization harder to achieve even if it does not affect the externally visible behavior in any way.
If the methods are defined as shown in your example, they are package private. So, if you create a test in the same package (though normally in a test directory) you will be able to access those methods and test them.
That said, if you can refactor or rewrite the class to be more easily testable then that might be a good idea. If indeed you have to test the results of the internal methods and can't just test public ones.
You should focus your test effort on public methods return value and not not on internal implementation.
Focusing on internal implementation causes tests to be harder to mantain since a basic refactoring not affecting the return value will probably require changing your tests.
Sometimes is impossible to avoid testing internal implementation since some methods return nothing and you need to "assert" something. In this case it seems you return something at some point, I'd focus on testing that.
It seems to me you have a (sadly common) misunderstanding of the word test; it does not mean 'execute from a test case'.
Testing means supplying a range of inputs, and asserting that the corresponding outputs are correct. 99% of the time that means checking return codes or object state, occasionally you have to use mocks to properly test a pure-output interface.
If you do that for the public methods, and the private methods are fully covered to the required standard, job done. If there is uncovered code in private methods, either use it to identify and add a missing test case, or delete it.
In the event you feel there would be something useful lost by deleting unreachable private code, make it public, or move it out to another class.
I have a junit test testArchive(). The Junit test tests the archive() method that archives a file and returns the url to it as a String. The URL is defined as an instance variable inside the Junit Test class.
class Prepare {
private String url = "";
#Test
public void testArchive() {
String url = getService().archive();
}
#Test
public void testSendEmail() {
getService().sendEmail(url) // Url is turning out to be null
}
} // end of class
I am writing another Junit test for sendEmail() which emails the URL. But the URL is turning out to be null, though its defined as a class variable
Can you please let me know how I need to correct my Junit test for send email?
Thank you
Short answer:
You should really not do that.
Detailed answer:
Unit tests (and therefore JUnit tests as well) are intended to run separately and independently from each other. Each test should check only one method, regardless of result of another method or another test. So in your case, method testSendEmail() should use some hard coded URL, or better few different URLs.
Keep in mind that:
Test cases should not have side effects: the .archive() looks like will produce side effects
Test cases should not assume an execution order of other test cases: your testSendEmail seems to assume testArchive is executed first, which is wrong
Test cases should not depend on external factors: the getService calls looks like an external (uncontrolled) factor
Test cases should be independent and self-contained
Instead of one test cases depending on the outcome of another,
you could use a private helper method that both test cases can call.
I removed the 2nd JUnit test and consolidated the tests into 1. Both archive and email will happen in one test.
I have a JUnit class with different methods to perform different tests.
I use Mockito to create a spy on real instance, and then override some method which is not relevant to the actual test I perform.
Is there a way, just for the sake of cleaning up after me in case some other tests that run after my tests also use the same instances and might execute a mocked method they didn't ask to mock, to un-mock a method?
say I have a spy object called 'wareHouseSpy'
say I overriden the method isSomethingMissing :
doReturn(false).when(wareHouseSpy).isSomethingMissing()
What will be the right way to un-override, and bring things back to normal on the spy i.e make the next invokation of isSomethingMissing to run the real method?
something like
doReturn(Mockito.RETURN_REAL_METHOD).when(wareHouseSpy).isSomethingSpy()
or maybe
Mockito.unmock(wareHouseSpy)
Who knows? I couldn't find nothing in that area
Thanks!
Assaf
I think
Mockito.reset(wareHouseSpy)
would do it.
Let's say most of your tests use the stubbed response. Then you would have a setUp() method that looks like this:
#Before
public void setUp() {
wareHouseSpy = spy(realWarehouse);
doReturn(false).when(wareHouseSpy).isSomethingMissing();
}
Now let's say you want to undo the stubbed response and use the real implementation in one test:
#Test
public void isSomethingMissing_useRealImplementation() {
// Setup
when(wareHouseSpy.isSomethingMissing()).thenCallRealMethod();
// Test - Uses real implementation
boolean result = wareHouseSpy.isSomethingMissing();
}
It depends whether you are testing with TestNG or JUnit.
JUnit creates a new instance of itself for each test method. You basically don't have to worry about reseting mocks.
With TestNG, you have to reset the mock(s) with Mockito.reset(mockA, mockB, ...) in either an #BeforeMethod or an #AfterMethod
The "normal" way is to re-instantiate things in your "setUp" method. However, if you have a real object that is expensive to construct for some reason, you could do something like this:
public class MyTests {
private static MyBigWarehouse realWarehouse = new MyBigWarehouse();
private MyBigWarehouse warehouseSpy;
#Before
public void setUp() {
warehouseSpy = spy(realWarehouse); // same real object - brand new spy!
doReturn(false).when(wareHouseSpy).isSomethingMissing();
}
#Test
...
#Test
...
#Test
...
}
Maybe I am not following but when you have a real object real:
Object mySpy = spy(real);
Then to "unspy" mySpy... just use real.
As per the documentation, we have
reset(mock);
//at this point the mock forgot any interactions & stubbing
The documentation specifies further
Normally, you don't need to reset your mocks, just create new mocks
for each test method. Instead of #reset() please consider writing
simple, small and focused test methods over lengthy, over-specified
tests.
Here's an example from their github repo which tests this behavior and uses it:
#Test
public void shouldRemoveAllInteractions() throws Exception {
mock.simpleMethod(1);
reset(mock);
verifyZeroInteractions(mock);
}
reference : ResetTest.java
Addressing this piece specifically:
Is there a way, just for the sake of cleaning up after me in case some other tests that run after my tests also use the same instances and might execute a mocked method they didn't ask to mock, to un-mock a method?
If you are using JUnit, the cleanest way to do this is to use #Before and #After (other frameworks have equivalents) and recreate the instance and the spy so that no test depends on or is impacted by whatever you have done on any other test. Then you can do the test-specific configuration of the spy/mock inside of each test. If for some reason you don't want to recreate the object, you can recreate the spy. Either way, everyone starts with a fresh spy each time.