I have a method inside my Dao class like this:
#Override
public List<Dog> loadAllDog(Date pDate) {
final MapSqlParameterSource lParameterSource = new MapSqlParameterSource();
lParameterSource.addValue("jdate", pDate);
final String lSql = readSqlQuery("LAD");
final NamedParameterJdbcTemplate lTemplate = createNamedParameterJdbcTemplate();
return lTemplate.query(lSql, lParameterSource, new DogExtractor());
}
I use the above method to load data for an integration test. Unfortunality the size of the result list is about 300000 data rows.
For my test it is ok to work only with 100 data rows. So I wrote a SQL Test file(Key LAD_TEST) that returns only 100 rows:
SELECT
*
FROM
DOG
WHERE
TO_CHAR(sell, 'dd.mm.yy') = TO_CHAR(:jdate,'dd.mm.yy')
and rownum <= 100
my question is, can I include anyhow that test sql(LAD_TEST) instead of the real production sql(LAD) without changing the production code here final String lSql = readSqlQuery("LAD"); ???
I am using jmockit in my testclass but that dao class(mDogDao) I am talking about is not mocked...
The call from my test:
List<Dog> lAllDog = mDogDao.loadAllDog(lNow.getTime());
Is there any way to manage this with jmockit without mocking mDogDao?
Some advice?
Thx
Stefan
You can mock the NamedParameterJdbcTemplate class, and record an expectation so the query(...) method returns your desired test data.
Why would you want to query your live database in a unit test?
What I always do is work against a seperate unit test database schema or an in-memory database. That way I am certain that a bug in my queries doesn't influence the data used by other people.
Even if you need a certain amount of test-data, you can always insert an extract of your data before your test and clean it up afterwards.
Moreover this way your unit tests are also run in isolation. If one tests modifies data, your other tests don't suffer from the possible consequences.
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.
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
This is the simple code that I have, and I have to write a jUnit test case for the same.
public String deletes() throws Exception {
String deleteSql = "select * from test";
if (isMultiTenant) {
return deleteSql = deleteSql + "and id = ? ";
} else {
return deleteSql;
}
}
so I did it in this way, but somewhere it doesn't look like a good test case its look like simply comparing string between two.
Is there any other way to write test case more meaningful
Most of the unit tests consist of three separate steps:
Data preparation
Execution
Results assertion
There are a lot of people who prefer a test consisted only of the assertion(s):
#Test
public void updateSqlTest() {
assertEquals(
"select * from test",
test.updateSQL()
);
}
This is one way of "looking" the test better in terms of formatting. That's all to discuss here as this kind of test makes no sense to me.
Remember: Don't aim for 100 % code coverage but covering the most important and business-related code. A more meaningful test would be:
Mocking the repository/DAO layer (accessing the database) and test the service layer performing business logic.
Running in-memory database for the purpose of testing (with Spring Boot usually H2 is used as it works very well), fetch and update the data from the database and assert the results.
The test you wrote makes no sense to me. I'd test out the behavior of the SQL query itself and the related business logic that uses it.
I'm developing a Spring REST application, and have a Service that access the database (using a Repository) and recover N last documents (I'm using MongoDB).
So, my Service has this function:
#Async
public CompletableFuture<List<MessageLogViewDto>> listLogs(int total) {
// Request the entities using PageRequest to list only last N registers ordered by DATE (DESC). I don't known a better way to do this!
PageRequest page = new PageRequest(0, total, Sort.Direction.DESC, "Date");
Page<MessageLog> messages = logRepo.findAll(page);
// Convert the Entity to DTO
List<MessageLogViewDto> messageList = messages.getContent().stream().map(
message -> convertDto.toMessageLogView(message)).collect(Collectors.toList());
return CompletableFuture.completedFuture(messageList);
}
(convertDto is a class to convert entity to Dto).
Ok, how to do a unit test of this?
I read on several blogs that a Unit Test should not connect to a database.
This unit test can be made very simple if I use a real test database, but I like to known what's the better way to do this test without a database (if possible).
I can mock the repository and the function findAll, but if I mock findAll method, I must mock the return value too. I don't know how to mock a Page<Object> response, because Page is a interface, and I will have to implement all the methods in the mock.
For my test, I was thinking in create 3 random message entity, with different dates, and check if only the last message was returned.
something like this:
#Test
public void shouldReturnLastMessage() {
int total = 1;
// mock findAll
// mock first entity
// mock a second entity
// mock a third entity
// ??? = must return a Page<MessageLog> with 3 entitys, how?
when(dbLogRepoMock.findAll(any(PageRequest.class))).thenReturn(???);
// call the service
List<MessageLogViewDto> result = logService.listLogs(total).join();
// check returned value
assertThat(result.size()).isEqualTo(total);
assertThat(result.get(0).getMensagem()).isEqualTo("third message"); // last message
// check if repository was called
verify(dbLogRepoMock).findAll(any(PageRequest.class));
}
Any help?
This may be a mildly controversial opinion, but I would focus on unit testing only your converter as there is no other meaningful custom logic in this method.
Looking at the method it does:
// 1 - Create an instance of a Spring class
PageRequest page = new PageRequest(0, total, Sort.Direction.DESC, "Date");
// 2 - Query a Spring Repository with the Spring Class
Page<MessageLog> messages = logRepo.findAll(page);
// 3 - CUSTOM LOGIC to convert result to custom DTO class
List<MessageLogViewDto> messageList = messages.getContent().stream().map(
message -> convertDto.toMessageLogView(message)).collect(Collectors.toList());
// 4 - Return a Java standard CompleteableFuture
return CompletableFuture.completedFuture(messageList);
Even if you were to write a unit test for this method,
1 is trivial class instantiation
2 uses result of 1 - but you'd be creating a mock response anyway so it wouldn't be meaningfully exercising 1.
3 is your converter logic, which you could write a standalone test for directly
4 is leveraging a core java feature that you are very very unlikely to find a bug with
Additionally, look at the signature of the method:
public CompletableFuture<List<MessageLogViewDto>> listLogs(int total) {
It accepts an integer total and returns a CompleteableFuture (the implementation of which we don't care to test as it's not the core feature of this method / is a built-in) containing a List of elements. Assumedly a list of size total.
Without actually accessing the database, you would have to mock the data returned to be the expected size. The actual logic of determining the size of the result is part of the data access framework, so you wouldn't even be able to validate that the output data size actually matches the input parameter of your method. Not much value there in my estimation.
I generally do not focus on unit-testing data-access methods that are just accessing and returning data without any meaningful logic. In this case the ONLY meaningful logic occurs in your converter, so I would focus on testing that.
you could possibly extract a method like:
List<MessageLogViewDTO> convertMessageLogsToMessageLogViewDTO(List<MessageLog> messages) {
return messages.getContent().stream().map(
message -> convertDto.toMessageLogView(message)).collect(Collectors.toList());
}
and then you can simply test that method with various cases (valid data, empty / null lists, etc.
My database of choice is MongoDB. I'm writing a data-layer API to abstract implementation details from client applications - that is, I'm essentially providing a single public interface (an object which acts as an IDL).
I'm testing my logic as I go in a TDD manner. Before each unit test, an #Before method is called to create a database singleton, after which, when the test completes, an #After method is called to drop the database. This helps to promote independence among unit tests.
Nearly all unit tests, i.e. performing a contextual query, require some kind of insertion logic to occur before hand. My public interface provides an insert method - yet, it seems incorrect to use this method as precursor logic to each unit test.
Really I need some kind of mocking mechanism, yet, I haven't had much experience with mocking frameworks, and it seems that Google returns nothing re a mocking framework one might use with MongoDB.
What do others do in these situations? That is, how do people unit test code that interacts with a database?
Also, my public interface connects to a database defined in a external configuration file - it seems incorrect to use this connection for my unit testing - again, a situation that would benefit from some kind of mocking?
Technically tests that talk to a database (nosql or otherwise) are not unit tests, as the tests are testing interactions with an external system, and not just testing an isolated unit of code. However tests that talk to a database are often extremely useful, and are often fast enough to run with the other unit tests.
Usually I have a Service interface (eg UserService) which encapsulates all the logic for dealing with the database. Code that relies on UserService can use a mocked version of UserService and is easily tested.
When testing the implementation of the Service that talks to Mongo, (eg MongoUserService) it is easiest to write some java code that will start/stop a mongo process on the local machine, and have your MongoUserService connect to that, see this question for some notes.
You could try to mock the functionality of the database while testing MongoUserService, but generally that is too error prone, and doesn't test what you really want to test, which is interaction with a real database. So when writing tests for MongoUserService, you set up a database state for each test. Look at DbUnit for an example of a framework for doing so with a database.
As sbridges wrote in this post it is a bad idea not to have a dedicated service (sometimes also known as repository or DAO) which abstracts the data access from the logic. Then you could test the logic by providing a mock of the DAO.
Another approach which I do is to create a Mock of the Mongo object (e.g. PowerMockito) and then return the appropriate results.
This because you don't have to test if the database works in unit tests but more over you should test if the right query was sent to the databse.
Mongo mongo = PowerMockito.mock(Mongo.class);
DB db = PowerMockito.mock(DB.class);
DBCollection dbCollection = PowerMockito.mock(DBCollection.class);
PowerMockito.when(mongo.getDB("foo")).thenReturn(db);
PowerMockito.when(db.getCollection("bar")).thenReturn(dbCollection);
MyService svc = new MyService(mongo); // Use some kind of dependency injection
svc.getObjectById(1);
PowerMockito.verify(dbCollection).findOne(new BasicDBObject("_id", 1));
That would also be an option. Of course the creation of the mocks and returning of the appropriate objects is just coded as an example above.
I wrote a MongoDB fake implementation in Java: mongo-java-server
Default is a in-memory backend, that can be easily used in Unit and Integration tests.
Example
MongoServer server = new MongoServer(new MemoryBackend());
// bind on a random local port
InetSocketAddress serverAddress = server.bind();
MongoClient client = new MongoClient(new ServerAddress(serverAddress));
DBCollection coll = client.getDB("testdb").getCollection("testcoll");
// creates the database and collection in memory and inserts the object
coll.insert(new BasicDBObject("key", "value"));
assertEquals(1, collection.count());
assertEquals("value", collection.findOne().get("key"));
client.close();
server.shutdownNow();
Today I think the best practice is to use testcontainers library (Java) or testcontainers-python port on Python. It allows to use Docker images with unit tests.
To run container in Java code just instantiate GenericContainer object (example):
GenericContainer mongo = new GenericContainer("mongo:latest")
.withExposedPorts(27017);
MongoClient mongoClient = new MongoClient(mongo.getContainerIpAddress(), mongo.getMappedPort(27017));
MongoDatabase database = mongoClient.getDatabase("test");
MongoCollection<Document> collection = database.getCollection("testCollection");
Document doc = new Document("name", "foo")
.append("value", 1);
collection.insertOne(doc);
Document doc2 = collection.find(new Document("name", "foo")).first();
assertEquals("A record can be inserted into and retrieved from MongoDB", 1, doc2.get("value"));
or on Python (example):
mongo = GenericContainer('mongo:latest')
mongo.with_bind_ports(27017, 27017)
with mongo_container:
def connect():
return MongoClient("mongodb://{}:{}".format(mongo.get_container_host_ip(),
mongo.get_exposed_port(27017)))
db = wait_for(connect).primer
result = db.restaurants.insert_one(
# JSON as dict object
)
cursor = db.restaurants.find({"field": "value"})
for document in cursor:
print(document)
I'm surprised no one advised to use fakemongo so far. It emulates mongo client pretty well, and it all runs on same JVM with tests - so integration tests become robust, and technically much more close to true "unit tests", since no foreign system interaction takes place. It's like using embedded H2 to unit test your SQL code.
I was very happy using fakemongo in unit tests that test database integration code in end-to-end manner. Consider this configuration in test spring context:
#Configuration
#Slf4j
public class FongoConfig extends AbstractMongoConfiguration {
#Override
public String getDatabaseName() {
return "mongo-test";
}
#Override
#Bean
public Mongo mongo() throws Exception {
log.info("Creating Fake Mongo instance");
return new Fongo("mongo-test").getMongo();
}
#Bean
#Override
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongo(), getDatabaseName());
}
}
With this you can test your code that uses MongoTemplate from spring context, and in combination with nosql-unit, jsonunit, etc. you get robust unit tests that cover mongo querying code.
#Test
#UsingDataSet(locations = {"/TSDR1326-data/TSDR1326-subject.json"}, loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
#DatabaseSetup({"/TSDR1326-data/dbunit-TSDR1326.xml"})
public void shouldCleanUploadSubjectCollection() throws Exception {
//given
JobParameters jobParameters = new JobParametersBuilder()
.addString("studyId", "TSDR1326")
.addString("execId", UUID.randomUUID().toString())
.toJobParameters();
//when
//next line runs a Spring Batch ETL process loading data from SQL DB(H2) into Mongo
final JobExecution res = jobLauncherTestUtils.launchJob(jobParameters);
//then
assertThat(res.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
final String resultJson = mongoTemplate.find(new Query().with(new Sort(Sort.Direction.ASC, "topLevel.subjectId.value")),
DBObject.class, "subject").toString();
assertThatJson(resultJson).isArray().ofLength(3);
assertThatDateNode(resultJson, "[0].topLevel.timestamp.value").isEqualTo(res.getStartTime());
assertThatNode(resultJson, "[0].topLevel.subjectECode.value").isStringEqualTo("E01");
assertThatDateNode(resultJson, "[0].topLevel.subjectECode.timestamp").isEqualTo(res.getStartTime());
... etc
}
I used fakemongo without problems with mongo 3.4 driver, and community is really close to release a version that supports 3.6 driver (https://github.com/fakemongo/fongo/issues/316).