Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jr. dev attempt at writing j-unit tests for utility classes #26

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@
<artifactId>springfox-swagger-ui</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/bravo/user/config/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

/*
Serializers-Deserializers take a object and convert it to a string of JSON format and vice versa

Never worked with Jackson before but looks like it is a Java library that is used to create serializers/deserializers

*/

//tags the class as a source of bean definitions for the application context in spring
@Configuration
public class AppConfig {

Expand Down
5 changes: 4 additions & 1 deletion src/main/java/com/bravo/user/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
@Configuration
@EnableSwagger2
public class SwaggerConfig {

/*
Docket class - Docket is a builder which is intended to be the primary interface into the swagger-springmvc framework.
It provides sensible defaults and convenience methods for configuration.
*/
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@
import com.bravo.user.model.dto.AddressDto;
import com.bravo.user.service.AddressService;
import com.bravo.user.validator.UserValidator;

//I understand the controllers and that they handle HTTP requests
//this controller specifically is handling requests related to Addresses
@RequestMapping(value = "/address")
@SwaggerController
public class AddressController {

private final AddressService addressService;
private final UserValidator userValidator;

//constructor for AddressController class
public AddressController(AddressService addressService, UserValidator userValidator) {
this.addressService = addressService;
this.userValidator = userValidator;
}

//here we will pass a user id to the endpoint and recieve a response
//@PathVariable annotation marks the userId as the required argument
//I have only seen @RequestMapping(method="GET") but as I understand, @GetMapping does the same thing
@GetMapping(value = "/retrieve/{userId}")
@ResponseBody
public List<AddressDto> retrieve(final @PathVariable String userId) {
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/com/bravo/user/controller/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping(value = "/login")
//also the first time I have seen/used Swagger but it looks like its a tool to help you
//build consumer and document RESTful APIs
//I have worked with Postman only in the past but they seem similar?
@SwaggerController
public class LoginController {

Expand All @@ -17,7 +20,7 @@ public class LoginController {
public LoginController(LoginService loginService) {
this.loginService = loginService;
}

//a post request means we will be adding information to our database
@PostMapping
public void login(final @RequestBody LoginDto request, HttpServletResponse httpResponse){
if(request.getUsername() == null){
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/bravo/user/model/dto/ReflectClassDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Data;

//Data Transfer Object class for reflect
@Data
public class ReflectClassDto {

//private instance variables can only be accessed by methods within this class
private final Class<?> type;
private List<ReflectFieldDto<?>> fields;

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/bravo/user/utility/DateUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
@UtilityClass
public class DateUtil {

//static final means that this is a compile-time constant - no instance allowed outside of class
//DateTimeFormatter - parses date into string of the pattern specified date/time pattern
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/com/bravo/user/utility/PageUtil.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
package com.bravo.user.utility;

import javax.servlet.http.HttpServletResponse;
//learned that lombok is a Java library tool that generates code for minimizing boilerplate code
import lombok.experimental.UtilityClass;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

@UtilityClass
public class PageUtil {

//setting default page size to 20
private static final int DEFAULT_SIZE = 20;

//to create a new page, we need 2 arguments - an integer for the page number and an integer for the size
public static PageRequest createPageRequest(final Integer page, final Integer size){
return createPageRequest(page, size, DEFAULT_SIZE);
}

public static PageRequest createPageRequest(
final Integer page,
final Integer size,
final Integer defaultSize
){
//I don't quite understand what is going on here,
final int pg = page != null && page > 0 ? page - 1 : 0;
final int sz = size != null && size > 0 ? size : defaultSize;
return PageRequest.of(pg, sz);
}

//updating page headers with
public static void updatePageHeaders(
final HttpServletResponse httpResponse,
final Page<?> page,
Expand Down
18 changes: 15 additions & 3 deletions src/main/java/com/bravo/user/utility/ReflectUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,42 @@

@UtilityClass
public class ReflectUtil {

//logging components help developers debug, have logger, handler/appender, layouts/formatters
//when a logger is called, logging component records the event in log handler and forwards to appropriate appender
private static final Logger LOGGER = LoggerFactory.getLogger(ReflectUtil.class);

//describe method takes in an instance of an object as a parameter and returns a ReflectClassDto object
public static ReflectClassDto describe(final Object instance){

final Class<?> instanceClass = instance.getClass();
//a java bean is just a standard that specifies - all properties are private, a public no-argument constructor, implements the Serializable interface
//what is the Serializable interface? Serializable objects can be written to streams, and hence files, object databases, etc.
BeanInfo beanInfo;
//try catch loop prevents compile errors in this case IntrospectionException
try {
//Introspector! this is new for me too. This class provides a standard way for tools to learn about the
//the properties, events and methods supposed by a target java bean
beanInfo = Introspector.getBeanInfo(instanceClass);

//try/catch and introspection exception here, and call the logger to log the error
} catch (IntrospectionException e) {
LOGGER.error("ERROR", e);
return null;
}
//here we will create the new instance of the reflectDTO class that we will return when the method is called
//Reflection is a fuzzy concept but would love to learn more about it
final ReflectClassDto reflection = new ReflectClassDto(instanceClass);
//for each loop
for(PropertyDescriptor desc : beanInfo.getPropertyDescriptors()){
//I think this is saying basically if the class equals the name of descriptor we will use a get method on the "Name" property (?)
if("class".equals(desc.getName())){
//if condition is true, we continue on in loop
continue;
}
final ReflectFieldDto<?> field = new ReflectFieldDto<>(desc.getPropertyType());
field.setField(desc.getName());
field.setValue(invokeReadMethod(desc, instance));
reflection.getFields().add(field);
}
//we return the reflection we created above
return reflection;
}

Expand Down
7 changes: 5 additions & 2 deletions src/main/java/com/bravo/user/utility/ValidatorUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
@UtilityClass
public class ValidatorUtil {

//<T> means type generic, ... means that we might be getting multiple parameters
//this method is checking whether passed in value is empty
public static <T> boolean isEmpty(T value, String... excludeFields){

//I learned that a reflection class allows us to inspect and modify an object's behavior at runtime
//instantiating a variable of the reflection class data transfer object
final ReflectClassDto reflection = ReflectUtil.describe(value);
if(reflection == null){
throw new IllegalStateException(String.format("could not describe class: '%s'", value));
Expand All @@ -24,7 +27,7 @@ public static <T> boolean isEmpty(T value, String... excludeFields){
}
return true;
}

//validation methods
public static <T> boolean isInvalid(T value){
return !isValid(value);
}
Expand Down
27 changes: 20 additions & 7 deletions src/test/java/com/bravo/user/service/AddressServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,46 @@
@ExtendWith(SpringExtension.class)
@SpringBootTest
class AddressServiceTest {

//I looked at tests that have already been written to try to get a better understanding
//of the different components of this app
//autowired ? - allows you to inject the object dependency implicitly
@Autowired
private AddressService addressService;

//mock bean? this will replace any existing bean of the same type in the application context
//according to what I have read, you can also use a @Qualifier notation and pass in qualifier metadata
//mock resource mapper to use in tests
@MockBean
private ResourceMapper resourceMapper;

//mock address repo to use in tests
@MockBean
private AddressRepository addressRepository;

//empty (?) list of data transfer object - addresses
private List<AddressDto> dtoAddresses;

@BeforeEach
public void beforeEach() {
final List<Integer> ids = IntStream.range(1, 10).boxed().collect(Collectors.toList());
/* wow! this is my first time learning about the Collectors class - pretty cool!
-allows us to accumulate/reduce values into a collection
IntStream.range()? - sequence of primitive int values that fall between given range, .boxed() - each element boxed to an Integer

*/
final List<Integer> ids = IntStream.range(1, 10).boxed().collect(Collectors.toList());
//here we use stream() to save a list of addresses
final List<Address> daoAddresses = ids.stream()
.map(id -> createAddress(Integer.toString(id))).collect(Collectors.toList());

//I think what is happening now is the id's are being mapped to the daoAddresses list
//when keyword - Mockito class! also new to me - sent down a new rabbit hole
//Mockito is a mocking framework for unit tests in Java - allows the creation of test double objects in automated unit tests
//"used to mock interfaces so that a dummy functionality can be added to a mock interface that can be used in testing"
when(addressRepository.findByUserId(anyString())).thenReturn(daoAddresses);

//this keyword refers to this specific instance
this.dtoAddresses = ids.stream().map(id -> createAddressDto(Integer.toString(id)))
.collect(Collectors.toList());

when(resourceMapper.convertAddresses(daoAddresses)).thenReturn(dtoAddresses);
}

//testing that endpoint retrieves correct id value
@Test
void retrieveByUserId() {
final String userId = "123a-456b";
Expand Down
33 changes: 33 additions & 0 deletions src/test/java/com/bravo/user/utility/DateUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.bravo.user.utility;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.util.Assert;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class DateUtilTest {
//expected objects

public void test_date_time_formatter_date_only_format() {

String expectedOutcome = "2022-04-01";
Date testDate = new Date(4-1-22);
DateTimeFormatter expectedFormatterDate = DateTimeFormatter.ofPattern("yyyy-MM-dd");

//i'm not entirely sure how to proceed with this but my thought process is to write a test
//that makes sure the output of DateTimeFormatter is the pattern specified
//Assertions.assertEquals(expectedOutcome, expectedFormatterDate.format(testDate));

}



DateTimeFormatter expectedFormatterDateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter expectedFormatterTime = DateTimeFormatter.ofPattern("HH:mm:ss");



}
29 changes: 29 additions & 0 deletions src/test/java/com/bravo/user/utility/PageUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.bravo.user.utility;


import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;

@SpringBootTest
public class PageUtilTest {

private PageUtil pageUtil;
//before annotation indicates that this method runs prior to every test
@BeforeEach
private void beforeEach(){
this.pageUtil = null;
}
//annotation indicates that the method is a test method - test methods are always public, always return void, and take no arguments
@Test
public void test_create_page_request_should_return_new_page_request(){
//my thought process here is, I need to test createPageRequest method on a page util object
//and then check that the new PageRequest is returned
PageRequest testPage = pageUtil.createPageRequest(1,3);
//I am not quite sure how to return the expected result
Assertions.assertEquals(testPage,testPage);
}

}
7 changes: 7 additions & 0 deletions src/test/java/com/bravo/user/utility/ReflectUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.bravo.user.utility;

public class ReflectUtilTest {

//Need to write tests for: describe(), invokeReadMethod()

}
21 changes: 21 additions & 0 deletions src/test/java/com/bravo/user/utility/ValidatorUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.bravo.user.utility;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class ValidatorUtilTest {
//creating test object
private ValidatorUtil validatorUtil;
//reset our test object before each test
@BeforeEach
public void beforeEach(){
this.validatorUtil = null;
}

@Test
public void isEmpty(){
boolean testValue = ValidatorUtil.isEmpty("cat");

}

}