I have written several Unit Tests and now switched to write Integration Test in our Java (Spring Boot) app. We use JUnit and Mockito libraries for testing.
As far as I know, Integration Tests check the entire rings rather than a function. However, I am confused that if I should also check the if conditions in the methods while integration testing. Here is an example service method:
#Override
public CountryDTO create(CountryRequest request) {
if (countryRepository.existsByCodeIgnoreCase(countryCode)) {
throw new EntityAlreadyExistsException();
}
final Country country = new Country();
country.setCode("UK");
country.setName("United Kingdom");
final Country created = countryRepository.save(country);
return new CountryDTO(created);
}
My questions are:
1. Can I write integration test for a Service or a Repository class?
2. when I test create method in my service above, I think I just create the proper request values (CountryRequest) in my Test class, then pass them to this create method and then check the returned value. Is that true? Or do I also need to test the condition in the if clause (countryRepository.existsByCodeIgnoreCase(countryCode))?
3. When I test find methods, I think I should first create record by calling create method and the proper place for this is #BeforeEach setup() {} method. Is that true?
If you wrote Unit tests that made sure, your services and repositories are working correctly (for example by validation and parameterized tests) I believe, you don't have to write integration tests for them.
You should write integration tests to check the behavior of your app. By testing if your controller is working correctly you will also check if service and repo are ok.
I believe unit test should check it.
Do you ask if you should create record in db? If you want to test if repository is correctly communicating with service and it with controller, you have to do it with some data.
Related
I'm trying to test a service method that lists all entities. The method looks like this:
#Test
public void listAllProfiles() {
Profile sampleProfile = Profile.createWithDefaultValues();
profileRepository.save(sampleProfile);
ProfileService profileService = new ProfileService(profileRepository, new ModelMapper());
List<ProfileDto> profiles = profileService.listAllProfiles();
ProfileDto lastProfile = profiles.get(profiles.size() - 1);
assertEquals(sampleProfile.getId(), lastProfile.getId());
}
The test fails cause IndexOutOfBoundsException: Index -1 out of bounds for length 0
I found out that the sampleProfile is not probably saved during the test. When I log profileRepository.findAll().size() the value is always 0.
What is worng with my test?
If you want to test your ProfileService you have to mock the profileRepository cause if you don't do that then you are actually doing an integration test.
Unit testing is all about testing small units if there are any dependencies on those units you have to mock them (either manually or with a framework, Mockito is the most common one in the Java world).
If your service is using the repository to fetch all the profiles you have to mock that call, something like this:
List<Profile> profilesList = Arrays.asList(new Profile("Profile 1"), new Profile("Proiile 2"));
given(profileRepository.findAll()).willAnswer((Answer<List>) invocation -> profilesList);
So you shouldn't save anything in the database (actually, you shouldn't interact with a database at all when you are unit testing), you just mock the repository you are using on your service. Here is a project I wrote some months ago where I actually solved the exact same issue.
I am attempting to use Spock to create an integration test around a Spring application. As it is not a Spring Boot application and the #SpringBootTest annotation interfered significantly with the app's initialization, I am using a minimal configuration.
I specifically need to stub a service in my app that returns objects of type Message; in the actual app these objects would come from a third-party vendor's library and they cannot be instantiated or subclassed, nor do their members have setters, so my only option is to create Stubs for them. However, with this current configuration (I've simplified the test significantly just to get the gist across):
#ContextConfiguration([TestSetup]) // supplies actual Spring beans including some JPA repos
class LogicSpec extends Specification {
#SpringBean
RestService restService = Stub()
#Autowired
ServiceUnderTest sut
#Autowired
SomeJPARepo repository;
def 'should do some business logic'() {
given:
Message m = new Stub() {
getStatus() >> "stubbed status"
}
restService.getMessage(_ as String) >> {
m
}
when:
sut.businessMethod()
then:
// just checking for side effects that `businessMethod` causes, no mocks being matched against
assert repository.findAll().every { it.processed == true }
}
}
}
Internally, the ServiceUnderTest.businessMethod() is using the Message object like this:
restService.getMessage(sid).getStatus() // should be "stubbed status"; always evaluates to null
however, every method of the Message stub always returns null regardless of whether I have defined a behavior for it. The Message objects must return specific values from its getters for the test to work. I would prefer to not have to declare every Message stub as its own #SpringBean; I need to eventually expand the test to use several different Message stub objects. I don't need mocks or spies because the number of invocations of RestService's methods doesn't matter, I just need it to emit proper stubs for ServiceUnderTest to chew on. Apologies if this question is unusual or I've missed something obvious; I'm slightly oblivious to Spock's notion of lifecycle, and the waters have been especially muddied with the addition of the Spring extension.
I discovered the answer soon after writing this, but just for posterity; the third-party Message class is declared as final and thus can't be subclassed; Spock was creating stubs for them but silently failing to add the overridden mock methods. I ended up using PowerMockito to remove this limitation; however this interfered with collecting test coverage metrics, so I instead used a wrapper class that can be mocked and used it everywhere in my code the original Message class was:
public class MessageWrapper {
public MessageWrapper(Message from) {...}
}
it's an extra bit of headache, but it was necessary because test coverage was required in this case. There also seems to be a promising Spock-specific mocking utility that will mock final classes, but I haven't tested it nor do I know if it will interfere with collecting coverage metrics like PowerMockito does.
I ran into some trouble testing a Spring app. The current approach in my team is to write scenarios in Gherkin and have Serenity provide its pretty reports.
A new component in the app will need a lot of test cases. The requirements will be provided in a few 'parsable' excel files so I thought it would be neat to just use them directly, row by row, in a Junit parametrized test. Another option would be to write a bloated Gherkin feature and tediously compose each example manually.
So I thought of something like that:
#RunWith(Parameterized.class)
private static class Tests {
#Parameterized.Parameters(name = "...") // name with the params
public static Collection params() {
// parse excel here or use some other class to do it
}
#Test
public void test() {
/* do the actual test - it involves sending and receiving some JSON objects */
}
}
This works smoothly but I ran into trouble trying to use
#RunWith(SerenityRunner.class)
The problem is that Junit does not support multiple runners. A solution I found is to make a nested class and annotate each with a different runner, but I don't know how to make it work (which runner should be on the outside, where do I actually run the tests, an so on).
Any thoughts?
Actually Serenity provides another runner - SerenityParameterizedRunner which seems to have the same features as JUnit's Parameterized.
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 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.