I have Struts 1 action and want to test it in isolation.
What this action do is as follows:
load data using parameters from request
build xml-based representation of this data
send this response directly to client
I use jMock for testing but have one doubt here.
My first test is
public void shouldActionInvocationPrintValidResponse() {
ProcessingAction action = new ProcessingAction();
DBService service = mock(DBService.class);
List records = new ArrayList();
when(service.loadData()).thenReturn(records);
ResponseBuilder builder = mock(ResponseBuilder.class);
when(builder.buildResponse(records)).thenReturn("fake response");
action.execute(null, null, null, null);
assertEquals("fake response", writer.getContentWritten());
}
And my prod code evaluated to this:
public String execute(...) {
List recordsList = service.loadData();
String response = responseBuilder.buildResponse(recordsList);
response.getWriter().print(response);
}
My doubt here is if such test isn't too big here. I check whole succesful flow here. Shouldn't there be separate tests for checking every single dependency call in their own tests?
I wonder because I had troubles with this test's name. My ideas at the beginning were something like
shouldFetchDataThenFormatThemAndSendResponse
As this is all the tests does, the name shows it probably does too much (look at the "and" e.g. in the test name)
And should I have whole test written at once, or just add dependencies calls step-by-step?
EDIT:
Detailed code for test and action provided.
I think you are on the right track. shouldFetchDataThenFormatThemAndSendResponse This says it all. In your test naming you are talking about implementation details. This is how your first test should have been written.
ProcessingAction action = new ProcessingAction();
Response response = action.execute();
assertEquals(true, response.IsValid);
Try: shouldGetResponseWhenActionExecuted.
Now you can look at how to get a response when executing an action.
I would bet you dollars to donuts that you didn't TDD this.
Remember: Intent over Implementation! Stop showing your crusty underwear.
It is hard to answer your question without seeing the code however I will give it a stab. For the test to be a Unit test, it should not exercise code other than the code in the class under test. If you have mocked every other class that the action calls and what you are verifying is only being done within the class under test, then no the test is not too big. I have written unit tests that have a large number of verification statements because all the things happen in the class under test due to the single invocation of the method.
My unit test rules are:
1. Exercise code only in the class under test
2. Only enter the method under test once per test method
I agree with John B.
Also, if you use the Mock Test Runner and write it correctly, you may not need an assertion.
Related
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.
Below is the codeSnippet of what i have to test:
the builder value is getting set but it is not being returned.
Please suggest me a way to test the below method.
public String convertMarkup(request)
{
Builder builder = new Response.builder();
String markUpData;
Map<String,List<String>>collectUrls= collectUrlMethod(request);
if(MapUtils.isNotEmpty(collectUrls)){
builder.setTrackerValue(collectUrls.get(Final_value).get(0));
builder.build();
}else{
markUpData="abc";
// rest of the code
}
return markUpData;
}
Tried changing the code around by introducing a new variable in input request and setting that variable instead of using a new builder. That worked.
public String convertMarkup(requestContext) {
Builder builder = new Response.builder();
String markUpData;
Map<String,List<String>>collectUrls= collectUrlMethod(request);
if(MapUtils.isNotEmpty(collectUrls)){
requestContext.setTrackerValue(collectUrls.get(Final_value).get(0));
}else{
markUpData="abc";
// rest of the code
}
return markUpData; }
builder isn't actually used, unless there's some side effect of calling builder.build(), which would be poor design, so you don't have to test it, and in fact you shouldn't test it.
The "unit" you are testing is the convertMarkup() method, which you test by giving it input and asserting its output - nothing more. Anything inside the method is implementation choice, and is free to be changed at any time, so your unit tests should not depend on it.
For example, a developer may change the implementation to:
public String convertMarkup(requestContext) {
return someExternalService.convertMarkup(requestContext);
}
after which all your tests should still compile and pass (assuming the external service works properly, of course).
Code coverage is another matter. Projects should set a minimum code coverage ratio and that may require more unit tests to exercise sufficient code paths, but they are likely valid unit test edges cases anyway.
I am new to JUnit mockito, I have this test function written for my Spring rest resource.
#Test
public void getAllMessageHappyTest() throws Exception {
List<Message> messageList = new ArrayList<>();
messageList.add(new Message(1,"Hello"));
messageList.add(new Message(5,"Hello world"));
messageList.add(new Message(3,"Hello World, G!"));
when(messageService.getAllMessages()).thenReturn(messageList);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/messages/").accept(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mockMvc.perform(requestBuilder).andReturn();
String expected = ""; // expected
JSONAssert.assertEquals(expected,mvcResult.toString(),false);
}
In the above scenario, I have the when(messageService.getAllMessages()).thenReturn(messageList); returning the messageList which is written by me(or by member of team) and I am comparing the returned JSON with the String expected which will also be written by me(or by the same member of team). So both the things are written by the same guy, so what is the point of having such kind of tests.
If I understand the question correctly the concern is this; because the person who writes the test also hardcodes (in the form of a JSON string) the expectation the test may be redundant or at least may be of limited value. Perhaps the sub text to your question is that since whoever wrote the underlying endpoint will provide the expectation then it must pass and if its success is preordained then it is of little value.
However, regardless of who writes the test and who writes the code-under-test, the example test you showed above has value because:
It tests more than the retuned JSON, it also tests ...
That the REST endpoint mapping is correct i.e. that it exposes an endpoint named "/messages/" which accepts JSON
The REST layer is using a serialiser which produces some JSON
Continued running of this test case will ensure that the expected behaviour of this endpoint continues to be met even after you (or some other member of your team) are no longer working on this code or, in other words; it acts as a regression safety net.
The code-under-test may be changed in future, if so then this test case provides a baseline against which future development can take place.
The test case provides a form of documentation for your code; people who are unfamiliar with this codebase can review the tests to understand how the code is expected to behave.
In addition, this test case could be extended to include tests for sad paths such as invalid repsonses, unsecured access attempts etc thereby improving test coverage.
Update 1: in response ot this comment:
even if someone makes changes in an actual code and now after making actual code is producing a different kind of JSON(say not as required) even then too test case will pass because when then is hardcoded and expected is also hardcoded. So what is the point?
A test like this clearly makes no sense:
String json = "...";
when(foo.getJson()).thenReturn(json);
assertEquals(json, foo.getJson());
Bu that is not what your test does. Instead your test asserts that the response - in the form of JSON - matches the serialised form of the response returned by your mocked messageService.getAllMessages(). So, your test covers the serialisation piece along with the various aspects of the Spring MVC layer such as the endpoint->controller mapping and interceptors and filters (if you have any).
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
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.