Unit test for SpringBoot Service PUT method - java

I'm writing Unit tests for my SpringBoot application and I'm completely lost in the PUT method. I'm getting Expecting code to raise a throwable but I guess the complete test is wrong.
Here's my code:
the PUT method in the Service
public void updateCar(String id, String carModel, Integer HP, Integer year, String designer) {
Garage garage = garageRepository.findById(id)
.orElseThrow(() -> new IllegalStateException(
"A car with the id " + id + " is not in our Garage."));
if(carModel != null && carModel.length() > 0 && !Objects.equals(garage.getCarModel(), carModel)) {
garage.setCarModel(carModel);
}
if(HP != null && !Objects.equals(garage.getHP(), HP)) {
garage.setHP(HP);
}
if(year != null && !Objects.equals(garage.getYear(), year)) {
garage.setYear(year);
}
if(designer != null && designer.length() > 0 && !Objects.equals(garage.getDesigner(), designer)) {
garage.setDesigner(designer);
}
garageRepository.save(garage);
}
My Repository:
#org.springframework.stereotype.Repository
public interface GarageRepository extends MongoRepository<Garage, String> {
Optional<Garage> findByCarModel(String carModel); }
And here's a test:
#Test
#DisplayName("Update Car Test")
void testUpdateCar() {
String id = "630ca281f12905d5f5249f08";
String carModel = "Shelby Cobra";
int HP = 485;
int year = 1964;
String designer = "Carroll Shelby";
Garage testGarage = new Garage();
testGarage.setId(id);
given(garageRepository.findById(testGarage.getId()))
.willReturn(Optional.of(testGarage));
assertThatThrownBy(() -> garageService.updateCar(id,carModel,HP,year,designer))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("A car with the id " + id + " is not in our Garage.");
verify(garageRepository.findById(id).get().getId());
}
Other tests are fine (Create, Read, and Delete), but just this one confuses the hell out of me. I don't think the Controller is necessary, so I skipped adding it.

You're setting up your test so that findById is returning an Optional containing testGarage. That is, something is actually being found.
The updateCar method only throws the IllegalStateException if findById returns an empty Optional - in other words, there's no object in the repository matching the search criterion.
So in this case, the exception is never thrown, and that's what makes the test fail.

Related

Java 8 Streams Nested ForEach with different conditions

i'm totally new to java 8 streams. just want know how to write the below code using java stream api. Not sure on how to write nested loops with filters to map the data.
public AccountByCustomerDto getAccountDetails(int customerId, HttpServletRequest request) throws Exception {
List<Accountowner> accountOwnerList = repo.getAccountOwners(customerId);
List<AccountByCustomerDto.AccountDto> aDtoList = new ArrayList<AccountByCustomerDto.AccountDto>();
for (Accountowner accountOwner : accountOwnerList) {
String currency = accountOwner.getAccount1().getAccountCurrency();
if(accountOwner != null && currency.startsWith("USD")) {
List<Accountbalance> accountBalanceList = accountOwner.getAccount1().getAccountbalances();
List<AccountByCustomerDto.BalancesDto> balanceDtoList = new ArrayList<AccountByCustomerDto.BalancesDto>();
for (Accountbalance balance : accountBalanceList) {
String creditInclude = balance.getCreditLimitIncluded();
if(balance != null && creditInclude.equals("Y")) {
AccountByCustomerDto.BalancesDto balanceDto = AccountByCustomerDto.BalancesDto.builder()
.balanceType(balance.getBalanceType()).baDto(null)
.referenceDate(
balance.getReferenceDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
.build();
balanceDtoList.add(balanceDto);
}
}
String accountId = Integer.toString(accountOwner.getAccount1().getAccountId()) + ":"
+ accountOwner.getAccount1().getAccountCurrency();
AccountByCustomerDto.AccountDto adto = AccountByCustomerDto.AccountDto.builder()
.accountId(Utility.encrypt(accountId))
.accountNumberDisplay(accountOwner.getAccount1().getAccountDisplay())
.balances(balanceDtoList).accountLink(null).build();
aDtoList.add(adto);
}
}
return AccountByCustomerDto.builder().accounts(aDtoList).build();
}
I think just switching the code to Stream would make the code even less readable. So when one method gets very long it's a good idea to split it into smaller methods.
When the code is split up into smaller methods streams provide a real benefit when it comes to readability compared to traditional loops.
Element in stream remains in stream if accountOwnerCurrencyIsUSD is true. The remaining elements are mapped to a AccountDto using createAccountDto and the result is collected into a AccountDto list. Is much easier to read than create list, loop through other list, get currency, check currency, create another list...
public AccountByCustomerDto getAccountDetails(int customerId, HttpServletRequest request) throws Exception {
List<AccountByCustomerDto.AccountDto> aDtoList = accountOwnerList.stream()
.filter(this::accountOwnerCurrencyIsUSD)
.map(this::createAccountDto)
.collect(Collectors.toList());
return AccountByCustomerDto.builder().accounts(aDtoList).build();
}
private AccountCustomerDto.AccountDto createAccountDto(Accountowner owner) {
String accountId = accountOwner.getAccount1().getAccountId() + ":" + accountOwner.getAccount1().getAccountCurrency();
List<AccountByCustomerDto.BalancesDto> balanceDtoList = accountOwner.getAccount1().getAccountbalances()
.stream()
.filter(this::includesCredit)
.map(this::createBalanceDto)
.collect(Collectors.toList());
return AccountByCustomerDto.AccountDto.builder()
.accountId(Utility.encrypt(accountId))
.accountNumberDisplay(accountOwner.getAccount1().getAccountDisplay())
.balances(balanceDtoList)
.accountLink(null)
.build();
}
private AccountByCustomerDto.BalancesDto createBalanceDto(Accountbalance balance) {
return AccountByCustomerDto.BalancesDto.builder()
.balanceType(balance.getBalanceType())
.baDto(null)
.referenceDate(alance.getReferenceDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
.build();
}
private boolean accountOwnerCurrencyIsUSD(Accountowner owner) {
return accountOwner != null && "USD".eqauls(accountOwner.getAccount1().getAccountCurrency());
}
private boolean includesCredit(Accountbalance balance) {
return balance != null && "Y".equals(balance.getCreditLimitIncluded());
}
I have not way to test the code, so take it with a grain of salt.

weird java behavior with nested closures losing captured variable values

OK this is going to be a bit long and complex, I'm hoping for some rubber ducking here.
I have this code, which is actually a bit more complex, but I think i did a reasonable job of simplifying it:
private Result getResult(Request request, RequestType type) {
final String date = request.getData(); // marker 1
final DataSource jdbcDataSource = getDataSource();
final JdbcOperations jdbcTemplate = newJdbcOperations(jdbcDataSource);
final TransactionTemplate transactionTemplate = createTransactionTemplate(jdbcDataSource);
Supplier<Integer> createResult = () -> transactionTemplate.execute(transactionStatus -> {
List<Map<String, Object>> rs = jdbcTemplate.queryForList("SELECT * FROM table");
if (rs.size() > 0) {
return ((Number) rs.get(0).get("count")).intValue();
} else {
log.info(request + " not found in table");
}
if (type == TYPE1) {
//...
} else {
throw new RuntimeException("unexpected type:" + type); // marker2
}
return 0;
});
int retryCount = 0;
while (retryCount < 5) {
try {
totalCount = createResult.get();
break;
} catch (DuplicateKeyException | DeadlockLoserDataAccessException e) {
// log
}
retryCount++;
}
}
This works fine, except that today the app server got into a state where it stopped working. The method enters, gets past line "marker1" so we know request is not null, then proceeds to log "null not found in table" suggesting that request turned into null and then throws runtimeException "marker2" because type has turned into null as well.
I hate to say it but this really just smells like a bug in JVM, but this really should be the last to consider, so I'm hoping that you, my dear rubber duck, would have some ideas.

Best way to check specific result of 2 methods in Java

I have the following method
public Message JavaMethod(String id1, String id2)
In which I need to call a Dao class's method to verify that an user with the provided Id exist, and if it does not, create a message detailing the Id that couldn't be found on the database with the following method:
createMessage("Message string",Enum.TYPE,IdofMissingUser);
At first I thought of doing it like this:
public Message JavaMethod(String id1, String id2) {
if(Dao.findUser(id1) == null || Dao.findUser(id2) == null){
return createMessage("Error",Enum.Error,id1);
}else{
//do some other stuff
}
}
But obviously this way I won't know which of the ids has not been found.
So I went ahead and created an ugly if else cycle:
public Message JavaMethod(String id1, String id2) {
if (Dao.findUser(id1) == null) {
return createMessage("Error", Enum.Error, id1);
} else if (Dao.findUser(id2) == null) {
return createMessage("Error", Enum.Error, id2);
} else {
// Do stuff after veryfing users exists
return createMessage("All OK", Enum.OK, messageData);
}
}
But I'm not feeling really confident that this is the best solution for this basic issue.
What would you guys recommend in this case?
You could wrap the ids in a list and use a for loop:
public Message someMethod(String id1, String id2) {
for (String id: Arrays.asList(id1, id2)) {
if (Dao.findUser(id) == null) {
return createMessage("Error", Enum.Error, id);
}
}
// Do stuff after verifying users exists
return createMessage("All OK", Enum.OK, messageData);
}
If you're only ever going to have two IDs, you could deal with a shorthand boolean. Question is whether that makes it less readable though. E.g.
public Message JavaMethod(String id1, String id2) {
User user1 = Dao.findUser(id1);
User user2 = Dao.findUser(id2);
if(user1 == null || user2 == null){
return createMessage("Error",Enum.Error,user1 == null ? id1 : id2);
}else{
//do some other stuff
}
}
This also doesn't deal with if both of the IDs were null, for that you could extend it:
public Message JavaMethod(String id1, String id2) {
User user1 = Dao.findUser(id1);
User user2 = Dao.findUser(id2);
if(user1 == null || user2 == null){
return createMessage("Error",Enum.Error,user1 == null && user2 == null? both : user1 == null ? id1 : id2);
}else{
//do some other stuff
}
}
You'd need to define what you would return for the both variable
More details on the shorthand boolean annotation can be found here

How to validate certain input parameters through a Validation API?

I have an API in which I am doing my own validation for certain input parameters. For example -
public Builder(int clientId) {
TestUtils.assertNonNegative(clientId, "ClientId");
this.clientId = clientId;
}
public Builder setUserId(String userid) {
TestUtils.assertNotNull(userid, "UserId");
this.userid = userid;
return this;
}
And my assertNonNegative and assertNotNull method in TestUtils class is like this -
public static void assertNonNegative(int val, String attr) {
if (val <= 0) {
s_logger.logError("Attribute = ", attr, " Value = ", val, " error=", attr, " cannot be negative or zero");
throw new IllegalArgumentException(attr + " cannot be negative or zero");
}
}
public static void assertNotNull(String value, String key) {
if (value == null || value.isEmpty()) {
s_logger.logError("Key = ", key, " Value = ", value, " error=", key,
" cannot be NULL or empty String");
throw new IllegalArgumentException(key + " cannot be null OR empty");
}
}
I am wondering is there any validation API available in any open source projects which I can use to substitute the above internal method I have? If yes, can anyone provide an example how would I achieve this? I still need to throw the same message back as an IllegalArgumentException
I dont understand why would you use an external API tu achieve a nullOrEmpty or a non-negative number validation but...
If you would like to verifiy an id of a user in a database directly in you Java app
This might interest you to learn:
http://www.mkyong.com/java/how-to-send-http-request-getpost-in-java/
Use a bit of PHP and verify if the user is in the database.
if(isset($_GET['idcmd']))
{
switch($_GET['idcmd'])
{
case 1:
if(isset($_POST['iduser']))
{
$sql= "SELECT idUser FROM users WHERE idUser=:iduser ";
$result = $db_conn->prepare($sql);
$result->bindParam(":iduser" ,$_POST['iduser']);
$result->execute();
$num=$result->fetchColumn();
if($num > 0){
echo "cool";
}else{
echo "nocool";
}
}
break;
}
}
Now if you make a POST request to the url www.mydomain.com/myapi.php?idcmd=1 and get the response cool, it means that the user is in database.
I hope it helps.

How to publish multiple event using EventQueues.publish in zk

Hope your problem is resolved, but my problem is still there
and I thought that you can help me to get out of this problem.
actually I had multiple events to publish one by one as per user
selection for eg: user select Season, Service, DateFrom and
DateTo and then clicks on the refresh button.
When the refresh button is clicked I had used the above logic to
get all the datas using the below mentioned code
public void onClick$ref(Event event){
if(lbox_service.getSelectedIndex() != 0 || lbox_season.getSelectedIndex() != 0)
{
if(lbox_service.getSelectedIndex() == 0)
{
setService_id("0");
}
else
{
setService_id(lbox_service.getSelectedItem().getValue().toString());
}
if(lbox_season.getSelectedIndex() == 0)
{
setSeason_id("0");
}
else
{
setSeason_id(lbox_season.getSelectedItem().getValue().toString());
}
System.out.println("Service Index 11 : "+ lbox_service.getSelectedIndex());
System.out.println("Season Index 11 : "+ lbox_season.getSelectedIndex());
EventQueue evtQ = EventQueues.lookup("myEventQueue", EventQueues.APPLICATION, true);
//evtQ.publish(new Event("service_id", self, lbox_service.getSelectedItem().getValue().toString()));
//evtQ.publish(new Event("season_id", self, lbox_season.getSelectedItem().getValue().toString()));
evtQ.publish(new Event("service_id", self, getService_id()));
evtQ.publish(new Event("season_id", self, getSeason_id()));
//evtQ.publish(new Event("onClickRef", null, lbox_service.getSelectedItem().getValue().toString()));
//evtQ.publish(new Event("onClickRef", null, lbox_season.getSelectedItem().getValue().toString()));
/*.publish(new Event("onClickRef", null, lbox_service.getSelectedItem().getValue().toString()));
EventQueues.lookup("myEventQu", EventQueues.DESKTOP, true).publish(new Event(
"onClickRef", null, lbox_season.getSelectedItem().getValue().toString()));*/
}
else
{
setService_id("0");
setSeason_id("0");
EventQueue evtQ = EventQueues.lookup("myEventQueue", EventQueues.APPLICATION, true);
evtQ.publish(new Event("service_id", self, getService_id()));
evtQ.publish(new Event("season_id", self, getSeason_id()));
System.out.println("Service Index : "+ lbox_service.getSelectedIndex());
System.out.println("Season Index : "+ lbox_season.getSelectedIndex());
}
}
now i had publish all my value and after that my new Controller
run that will subscribe those published values. using the
below code
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
EventQueues.lookup("myEventQueue", EventQueues.APPLICATION, true).subscribe(new EventListener() {
public void onEvent(Event event) throws Exception {
/*String service = (String) event.getData();
logger.info("Servive $$$$$$$$$ " + service);
//String season = (String) event.getData();
//logger.info("Season $$$$$$$$$ " + season); */
if("service_id".equals(event.getName())) {
setService_id((String) event.getData());
baseController.setFilter_bar(true);
System.out.println("Service Id :" +event.getData());
}
else if("season_id".equals(event.getName())) {
setSeason_id((String) event.getData());
baseController.setFilter_bar(true);
System.out.println("Season Id :" +event.getData());
}
/*setService_id((String) event.getData());
setSeason_id((String) event.getData());*/
/*if("season_id".equals(event.getName())){
setSeason_id((String) event.getData());
}else
{
setSeason_id("0");
}*/
System.out.println("Filter bar :" +baseController.isFilter_bar());
if(baseController.isFilter_bar() == true)
{
String dateFrom = "";
String dateTo = "";
String order = "2";
List TDRetailers = verificationStoreHibernateDao.getTraditionalRetailers(
getService_id(), getSeason_id(), dateFrom, dateTo, order);
//VerificationStoreHibernateDao storeHibernateDao = new VerificationStoreHibernateDao();
//List TDRetailers = this.verificationStoreHibernateDao.getTraditionalRetailers(service_id);
//ListModel listModel = this.retailers.getModel();
ListModelList listModelList = (ListModelList) retailer.getModel();
listModelList.clear();
listModelList.addAll(TDRetailers);
baseController.setFilter_bar(true);
}
}
});
}
but actully my problem is with running the query and with
getting those published values. Based on them I will be able to
run my Traditional getTraditionalRetailers queries.
My problem is
how to publish multiple events values. Is it the right way
that I had done.
as I had done separate publish, everytime
I publish new value The query runs, the result is that i had
mutiple time query execution. for example If i will publish two
values the queries run's for the two times and if I publish
three values the query executes for three time.
I don't know what is their problem. Help me to solve my error.
The event object passed through EventQueue is where you put your payload there. You can just define an aggregate Event class and collect information and publish them in a whole.
If you can publish all information in a whole(using an aggregate Event), this is solved automatically.

Categories

Resources