findAll();
12 |
13 | User findById(long id);
14 |
15 | void save(User user);
16 | }
17 |
--------------------------------------------------------------------------------
/account-rest-api/src/main/java/com/bobocode/config/WebConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | /**
4 | * This class provides web (servlet) related configuration.
5 | *
6 | * todo: 1. Mark this class as Spring config class
7 | * todo: 2. Enable web mvc using annotation
8 | * todo: 3. Enable component scanning for package "web" using annotation value
9 | */
10 | public class WebConfig {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/account-jsp/src/main/java/com/bobocode/config/WebConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | /**
4 | * This class provides web (servlet) related configuration.
5 | *
6 | * todo: 1. Mark this class as Spring config class
7 | * todo: 2. Enable web mvc using annotation
8 | * todo: 3. Enable component scanning for package "web" using annotation value
9 | * todo: 4. Configure JPS internal view resolver with prefix = "/WEB-INF/views/" and suffix ".jsp"
10 | */
11 | public class WebConfig {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/account-jsp/src/main/java/com/bobocode/web/controller/WelcomeController.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.web.controller;
2 |
3 | /**
4 | * Welcome controller that consists of one method that handles get request to "/" and "/welcome" and respond with a message.
5 | *
6 | * todo: 1. Mark this class as Spring controller
7 | * todo: 2. Configure HTTP GET mapping "/" and "/welcome" for method {@link WelcomeController#welcome()}
8 | * todo: 3. Forward the request to "welcome.jsp" view
9 | */
10 | public class WelcomeController {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/test/java/com/bobocode/WebAppInitializerWrapper.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.WebAppInitializer;
4 |
5 | class WebAppInitializerWrapper extends WebAppInitializer {
6 | public String[] getServletMappings() {
7 | return super.getServletMappings();
8 | }
9 |
10 | public Class[] getRootConfigClasses() {
11 | return super.getRootConfigClasses();
12 | }
13 |
14 | public Class>[] getServletConfigClasses() {
15 | return super.getServletConfigClasses();
16 | }
17 | }
--------------------------------------------------------------------------------
/account-jsp/src/test/java/com/bobocode/util/WebAppInitializerWrapper.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.util;
2 |
3 | import com.bobocode.config.AccountWebAppInitializer;
4 |
5 | public class WebAppInitializerWrapper extends AccountWebAppInitializer {
6 | public String[] getServletMappings() {
7 | return super.getServletMappings();
8 | }
9 |
10 | public Class[] getRootConfigClasses() {
11 | return super.getRootConfigClasses();
12 | }
13 |
14 | public Class>[] getServletConfigClasses() {
15 | return super.getServletConfigClasses();
16 | }
17 | }
--------------------------------------------------------------------------------
/hello-application-context/src/main/java/com/bobocode/config/AppConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import com.bobocode.TestDataGenerator;
4 |
5 | /**
6 | * This class application context configuration.
7 | *
8 | * todo: make this class a Spring configuration class
9 | * todo: enable component scanning for dao and service packages
10 | * todo: provide explicit configuration for a bean of type {@link TestDataGenerator} with name "dataGenerator" in this class.
11 | * todo: Don't specify bean name "dataGenerator" explicitly
12 | */
13 | public class AppConfig {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/main/java/com/bobocode/config/RootConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
5 |
6 | /**
7 | * This class provides application root (non-web) configuration.
8 | *
9 | * todo: mark this class as config
10 | * todo: enable component scanning for all packages in "com.bobocode"
11 | * todo: ignore all web related config and beans (ignore @{@link Controller}, ignore {@link EnableWebMvc}) using exclude filter
12 | */
13 | public class RootConfig {
14 | }
15 |
--------------------------------------------------------------------------------
/account-rest-api/src/main/java/com/bobocode/config/RootConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
5 |
6 | /**
7 | * This class provides application root (non-web) configuration.
8 | *
9 | * todo: 1. Mark this class as config
10 | * todo: 2. Enable component scanning for all packages in "com.bobocode" using annotation property "basePackages"
11 | * todo: 3. Exclude web related config and beans (ignore @{@link Controller}, ignore {@link EnableWebMvc})
12 | */
13 | public class RootConfig {
14 | }
15 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/main/java/com/bobocode/web/controller/WelcomeController.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.web.controller;
2 |
3 | /**
4 | * Welcome controller that consists of one method that handles get request to "/welcome" and respond with a message.
5 | *
6 | * todo: mark this class as Spring controller
7 | * todo: configure HTTP GET mapping "/welcome" for method {@link WelcomeController#welcome()}
8 | * todo: tell Spring that {@link WelcomeController#welcome()} method provides response body
9 | */
10 | public class WelcomeController {
11 |
12 | public String welcome() {
13 | return "Welcome to Spring MVC!";
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/inter-bean-dependencies/src/main/java/com/bobocode/dao/impl/FakeAccountDao.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.dao.impl;
2 |
3 | import com.bobocode.TestDataGenerator;
4 | import com.bobocode.dao.AccountDao;
5 | import com.bobocode.model.Account;
6 | import lombok.Getter;
7 | import lombok.RequiredArgsConstructor;
8 |
9 | @Getter
10 | @RequiredArgsConstructor
11 | public class FakeAccountDao implements AccountDao {
12 | private final TestDataGenerator dataGenerator;
13 |
14 | @Override
15 | public Account findById(Long id) {
16 | Account account = dataGenerator.generateAccount();
17 | account.setId(id);
18 | return account;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/spring-framework-exercises-model/src/main/java/com/bobocode/model/Account.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.model;
2 |
3 | import lombok.*;
4 |
5 | import java.math.BigDecimal;
6 | import java.time.LocalDate;
7 | import java.time.LocalDateTime;
8 |
9 | @NoArgsConstructor
10 | @Getter
11 | @Setter
12 | @ToString
13 | @EqualsAndHashCode(of = "email")
14 | public class Account {
15 | private Long id;
16 | private String firstName;
17 | private String lastName;
18 | private String email;
19 | private LocalDate birthday;
20 | private Gender gender;
21 | private LocalDateTime creationTime;
22 | private BigDecimal balance = BigDecimal.ZERO;
23 | }
24 |
--------------------------------------------------------------------------------
/transactional-user-service/src/main/java/com/bobocode/config/RootConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.transaction.PlatformTransactionManager;
4 |
5 | /**
6 | * This class provides root application configuration. It scans for all available beans and enables declarative
7 | * transaction management.
8 | *
9 | * todo: 1. Mark this class as config
10 | * todo: 2. Enable component scanning for package "com.bobocode"
11 | * todo: 3. Configure JPA {@link PlatformTransactionManager} with bean name "transactionManager"
12 | * todo: 4. Enable declarative transaction management
13 | */
14 | public class RootConfig {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/account-rest-api/src/main/java/com/bobocode/config/AccountRestApiInitializer.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
4 |
5 | public class AccountRestApiInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
6 | @Override
7 | protected Class>[] getRootConfigClasses() {
8 | return new Class[]{RootConfig.class};
9 | }
10 |
11 | @Override
12 | protected Class>[] getServletConfigClasses() {
13 | return new Class[]{WebConfig.class};
14 | }
15 |
16 | @Override
17 | protected String[] getServletMappings() {
18 | return new String[]{"/"};
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/account-jsp/src/main/java/com/bobocode/config/RootConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import com.bobocode.TestDataGenerator;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
6 |
7 | /**
8 | * This class provides application root (non-web) configuration.
9 | *
10 | * todo: 1. Mark this class as config
11 | * todo: 2. Enable component scanning for all packages in "com.bobocode" using annotation property "basePackages"
12 | * todo: 3. Exclude web related config and beans (ignore @{@link Controller}, ignore {@link EnableWebMvc})
13 | * todo: 4. Configure {@link TestDataGenerator} bean with name "dataGenerator" (don't specify name explicitly)
14 | */
15 | public class RootConfig {
16 | }
17 |
--------------------------------------------------------------------------------
/inter-bean-dependencies/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-framework-exercises
7 | com.bobocode
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | bean-configuration-exercise
13 |
14 |
15 |
16 | com.bobocode
17 | spring-framework-exercises-util
18 | 1.0-SNAPSHOT
19 |
20 |
21 |
--------------------------------------------------------------------------------
/hello-application-context/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-framework-exercises
7 | com.bobocode
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | hello-application-context
13 |
14 |
15 |
16 | com.bobocode
17 | spring-framework-exercises-util
18 | 1.0-SNAPSHOT
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/spring-framework-exercises-model/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-framework-exercises
7 | com.bobocode
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-framework-exercises-model
13 |
14 |
15 |
16 | org.hibernate.javax.persistence
17 | hibernate-jpa-2.1-api
18 | 1.0.2.Final
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/account-jsp/src/main/java/com/bobocode/web/controller/AccountController.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.web.controller;
2 |
3 | import com.bobocode.TestDataGenerator;
4 | import com.bobocode.model.Account;
5 |
6 | /**
7 | * This controller provides endpoint that generates a list of {@link Account} and passes it to the view.
8 | *
9 | * todo: 1. Configure controller that handles requests with url "/accounts"
10 | * todo: 2. Inject a {@link TestDataGenerator}
11 | * todo: 3. Implement a method that handles GET request with param "size" to url "/accounts" and forwards it to the accounts.jsp view
12 | * todo: 4. In this method generate a list of account using data generator and received int value "size"
13 | * todo: 5. Provide a default value "10" for parameter "size"
14 | * todo: 6. Pass the list of accounts to the view using model attribute with name "accountList"
15 | */
16 | public class AccountController {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/inter-bean-dependencies/src/main/java/com/bobocode/config/RootConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import com.bobocode.TestDataGenerator;
4 | import com.bobocode.dao.AccountDao;
5 | import com.bobocode.dao.impl.FakeAccountDao;
6 | import com.bobocode.service.AccountService;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.stereotype.Component;
9 |
10 | /**
11 | * todo: Refactor {@link RootConfig} in order to user inter-bean dependencies properly.
12 | */
13 | @Component
14 | public final class RootConfig {
15 |
16 | @Bean
17 | public AccountService accountService() {
18 | return new AccountService(fakeAccountDao());
19 | }
20 |
21 | @Bean
22 | public final AccountDao fakeAccountDao() {
23 | return new FakeAccountDao(dataGenerator());
24 | }
25 |
26 | @Bean
27 | private TestDataGenerator dataGenerator() {
28 | return new TestDataGenerator();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/hello-application-context/src/main/java/com/bobocode/service/AccountService.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.service;
2 |
3 | import com.bobocode.dao.AccountDao;
4 | import com.bobocode.model.Account;
5 |
6 | import java.util.Comparator;
7 | import java.util.List;
8 |
9 | /**
10 | * Provides service API for {@link Account}.
11 | *
12 | * todo: configure {@link AccountService} bean implicitly using special annotation for service classes
13 | * todo: use implicit constructor-based dependency injection (don't use {@link org.springframework.beans.factory.annotation.Autowired})
14 | */
15 | public class AccountService {
16 | private final AccountDao accountDao;
17 |
18 | public AccountService(AccountDao accountDao) {
19 | this.accountDao = accountDao;
20 | }
21 |
22 | public Account findRichestAccount() {
23 | List accounts = accountDao.findAll();
24 | return accounts.stream()
25 | .max(Comparator.comparing(Account::getBalance))
26 | .get();
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/spring-framework-exercises-util/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-framework-exercises
7 | com.bobocode
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-framework-exercises-util
13 |
14 |
15 |
16 |
17 | com.bobocode
18 | spring-framework-exercises-model
19 | 1.0-SNAPSHOT
20 |
21 |
22 | io.codearte.jfairy
23 | jfairy
24 | 0.5.7
25 |
26 |
27 |
--------------------------------------------------------------------------------
/hello-application-context/src/main/java/com/bobocode/dao/FakeAccountDao.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.dao;
2 |
3 | import com.bobocode.TestDataGenerator;
4 | import com.bobocode.model.Account;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 |
7 | import java.util.List;
8 | import java.util.stream.Stream;
9 |
10 | import static java.util.stream.Collectors.toList;
11 |
12 | /**
13 | * Provides a fake {@link AccountDao} implementation that uses generated fake data.
14 | *
15 | * todo: configure this class as Spring component with bean name "accountDao"
16 | * todo: use explicit (with {@link Autowired} annotation) constructor-based dependency injection
17 | */
18 | public class FakeAccountDao implements AccountDao {
19 | private List accounts;
20 |
21 | public FakeAccountDao(TestDataGenerator testDataGenerator) {
22 | this.accounts = Stream.generate(testDataGenerator::generateAccount)
23 | .limit(20)
24 | .collect(toList());
25 | }
26 |
27 | @Override
28 | public List findAll() {
29 | return accounts;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/inter-bean-dependencies/src/test/java/com/bobocode/RootConfigContextTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.dao.impl.FakeAccountDao;
5 | import com.bobocode.service.AccountService;
6 | import org.junit.jupiter.api.Test;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
9 |
10 | import static org.assertj.core.api.Assertions.assertThat;
11 |
12 |
13 | @SpringJUnitConfig(classes = RootConfig.class)
14 | class RootConfigContextTest {
15 |
16 | @Autowired
17 | private AccountService accountService;
18 |
19 | @Autowired
20 | private FakeAccountDao accountDao;
21 |
22 | @Autowired
23 | private TestDataGenerator dataGenerator;
24 |
25 | @Test
26 | void dataGeneratorShouldHaveScopeSingleton() {
27 | assertThat(accountDao.getDataGenerator()).isEqualTo(dataGenerator);
28 | }
29 |
30 | @Test
31 | void accountDaoShouldHaveScopeSingleton() {
32 | assertThat(accountService.getAccountDao()).isEqualTo(accountDao);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/main/java/com/bobocode/config/WebAppInitializer.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
4 |
5 | /**
6 | * This class is used to configure DispatcherServlet and links it with application config classes
7 | *
8 | * todo: provide default servlet mapping ("/")
9 | * todo: use {@link WebConfig} as ServletConfig class
10 | * todo: use {@link RootConfig} as root application config class
11 | */
12 | public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
13 | @Override
14 | protected Class>[] getRootConfigClasses() {
15 | throw new UnsupportedOperationException("Method is not implemented yet!");
16 | }
17 |
18 | @Override
19 | protected Class>[] getServletConfigClasses() {
20 | throw new UnsupportedOperationException("Method is not implemented yet!");
21 | }
22 |
23 | @Override
24 | protected String[] getServletMappings() {
25 | throw new UnsupportedOperationException("Method is not implemented yet!");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/account-jsp/src/main/java/com/bobocode/config/AccountWebAppInitializer.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
4 |
5 | /**
6 | * This class is used to configure DispatcherServlet and links it with application config classes
7 | *
8 | * todo: provide default servlet mapping ("/")
9 | * todo: use {@link WebConfig} as ServletConfig class
10 | * todo: use {@link RootConfig} as root application config class
11 | */
12 | public class AccountWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
13 | @Override
14 | protected Class>[] getRootConfigClasses() {
15 | throw new UnsupportedOperationException("It's your job to implement this method!");
16 | }
17 |
18 | @Override
19 | protected Class>[] getServletConfigClasses() {
20 | throw new UnsupportedOperationException("It's your job to implement this method!");
21 | }
22 |
23 | @Override
24 | protected String[] getServletMappings() {
25 | throw new UnsupportedOperationException("It's your job to implement this method!");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/transactional-user-service/src/main/java/com/bobocode/dao/impl/JpaUserDao.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.dao.impl;
2 |
3 | import com.bobocode.dao.UserDao;
4 | import com.bobocode.model.jpa.User;
5 | import org.springframework.stereotype.Repository;
6 | import org.springframework.transaction.annotation.Transactional;
7 |
8 | import javax.persistence.EntityManager;
9 | import javax.persistence.PersistenceContext;
10 | import java.util.List;
11 |
12 | /**
13 | * This class implements {@link UserDao} using JPA.
14 | *
15 | * todo: 1. Configure {@link JpaUserDao} bean as Spring Repository with name "userDao"
16 | * todo: 2. Enable transaction management on class level
17 | * todo: 3. Inject {@link EntityManager} using @{@link PersistenceContext} annotation
18 | */
19 | public class JpaUserDao implements UserDao {
20 | private EntityManager entityManager;
21 |
22 | @Override
23 | public List findAll() {
24 | return entityManager.createQuery("select u from User u", User.class).getResultList();
25 | }
26 |
27 | @Override
28 | public User findById(long id) {
29 | return entityManager.find(User.class, id);
30 | }
31 |
32 | @Override
33 | public void save(User user) {
34 | entityManager.persist(user);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/account-rest-api/src/main/java/com/bobocode/web/controller/AccountRestController.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.web.controller;
2 |
3 | import com.bobocode.dao.AccountDao;
4 |
5 | /**
6 | *
7 | * todo: 1. Configure rest controller that handles requests with url "/accounts"
8 | * todo: 2. Inject {@link AccountDao} implementation
9 | * todo: 3. Implement method that handles GET request and returns a list of accounts
10 | * todo: 4. Implement method that handles GET request with id as path variable and returns account by id
11 | * todo: 5. Implement method that handles POST request, receives account as request body, saves account and returns it
12 | * todo: Configure HTTP response status code 201 - CREATED
13 | * todo: 6. Implement method that handles PUT request with id as path variable and receives account as request body.
14 | * todo: It check if account id and path variable are the same and throws {@link IllegalStateException} otherwise.
15 | * todo: Then it saves received account. Configure HTTP response status code 204 - NO CONTENT
16 | * todo: 7. Implement method that handles DELETE request with id as path variable removes an account by id
17 | * todo: Configure HTTP response status code 204 - NO CONTENT
18 | */
19 | public class AccountRestController {
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/hello-spring-mvc/README.MD:
--------------------------------------------------------------------------------
1 | #
Hello Spring MVC exercise :muscle:
2 | Improve your *Spring MVC* configuration skills
3 | ### Task
4 | The task is to **configure a typical *Spring MVC* application** separating web and root configs. Your job is to follow
5 | the instructions in the *todo* section and **implement a proper configuration.**
6 |
7 | To verify your configuration, run `WelcomeWebAppTest.java`
8 |
9 |
10 | ### Pre-conditions :heavy_exclamation_mark:
11 | You're supposed to be familiar with *Spring MVC*
12 |
13 | ### How to start :question:
14 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
15 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
16 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
17 |
18 | ### Related materials :information_source:
19 | * [Spring MVC basics tutorial](https://github.com/boy4uck/spring-framework-tutorial/tree/master/spring-framework-mvc-basics)
20 |
21 |
--------------------------------------------------------------------------------
/transactional-user-service/src/main/java/com/bobocode/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.service;
2 |
3 | import com.bobocode.dao.UserDao;
4 | import com.bobocode.model.jpa.Role;
5 | import com.bobocode.model.jpa.RoleType;
6 | import com.bobocode.model.jpa.User;
7 |
8 | import java.util.List;
9 |
10 | import static java.util.stream.Collectors.toList;
11 |
12 | /**
13 | * This class proovides {@link User} related service logic.
14 | *
15 | * todo: 1. Configure {@link UserService} bean as spring service
16 | * todo: 2. Inject {@link UserDao} using constructor-based injection
17 | * todo: 3. Enable transaction management on class level
18 | * todo: 4. Configure {@link UserService#getAll()} as read-only method
19 | * todo: 4. Configure {@link UserService#getAllAdmins()} as read-only method
20 | */
21 | public class UserService {
22 | private UserDao userDao;
23 |
24 | public void save(User user) {
25 | userDao.save(user);
26 | }
27 |
28 | public List getAll() {
29 | return userDao.findAll();
30 | }
31 |
32 | public List getAllAdmins() {
33 | throw new UnsupportedOperationException("Don't be lazy and implement the method");
34 | }
35 |
36 | public void addRole(Long userId, RoleType roleType) {
37 | User user = userDao.findById(userId);
38 | Role role = Role.valueOf(roleType);
39 | user.addRole(role);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/spring-framework-exercises-model/src/main/java/com/bobocode/model/jpa/Role.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.model.jpa;
2 |
3 | import lombok.Getter;
4 | import lombok.NoArgsConstructor;
5 | import lombok.Setter;
6 | import lombok.ToString;
7 |
8 | import javax.persistence.*;
9 | import java.time.LocalDateTime;
10 | import java.util.Objects;
11 |
12 | @NoArgsConstructor
13 | @Getter
14 | @Setter
15 | @ToString(exclude = "user")
16 | @Entity
17 | @Table(name = "role")
18 | public class Role {
19 | @Id
20 | @GeneratedValue
21 | private Long id;
22 |
23 | @Enumerated(EnumType.STRING)
24 | @Column(name = "role_type")
25 | private RoleType roleType;
26 |
27 | @Column(name = "creation_date")
28 | private LocalDateTime creationDate = LocalDateTime.now();
29 |
30 | @ManyToOne
31 | @JoinColumn(name = "user_id")
32 | private User user;
33 |
34 | public static Role valueOf(RoleType roleType) {
35 | return new Role(roleType);
36 | }
37 |
38 | private Role(RoleType roleType) {
39 | this.roleType = roleType;
40 | }
41 |
42 | @Override
43 | public boolean equals(Object o) {
44 | if (this == o) return true;
45 | if (!(o instanceof Role)) return false;
46 |
47 | Role role = (Role) o;
48 |
49 | return Objects.equals(id, role.id);
50 | }
51 |
52 | @Override
53 | public int hashCode() {
54 | return 31;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/hello-application-context/README.MD:
--------------------------------------------------------------------------------
1 | #
Hello ApplicationContext exercise :muscle:
2 | Improve your *Spring ApplicationContext* Java configuration skills
3 | ### Task
4 | The task is to **configure `ApplicationContext`** that contains `AccountService` bean, `AccountDao` bean
5 | and `TestDataGenerator` bean. Your job is to follow the instructions in the *todo* section and **implement
6 | a proper configuration.**
7 |
8 | To verify your configuration, run `AppConfigTest.java`
9 |
10 |
11 | ### Pre-conditions :heavy_exclamation_mark:
12 | You're supposed to be familiar with *Spring IoC* and *Dependency injection*
13 |
14 | ### How to start :question:
15 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
16 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
17 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
18 |
19 | ### Related materials :information_source:
20 | * [Spring IoC basics tutorial](https://github.com/boy4uck/spring-framework-tutorial/tree/master/spring-framework-ioc-basics)
21 |
22 |
--------------------------------------------------------------------------------
/account-jsp/README.MD:
--------------------------------------------------------------------------------
1 | #
Accounts JSP exercise :muscle:
2 | Improve your *Spring MVC* configuration and controller mapping skills
3 | ### Task
4 | This is a typical *Spring MVC* application with *JSP pages*. Your job is to **configure dispatcher servlet initializer**
5 | separating root and web contexts, **configure `ViewResolver`** for *JSP* views, and **implement two controllers
6 | `WelcomeController` and `AccountController`**
7 |
8 | To verify your configuration, run all tests from test package of this module
9 |
10 |
11 | ### Pre-conditions :heavy_exclamation_mark:
12 | You're supposed to be familiar with *Spring MVC*
13 |
14 | ### How to start :question:
15 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
16 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
17 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
18 |
19 | ### Related materials :information_source:
20 | * [Spring MVC basics tutorial](https://github.com/boy4uck/spring-framework-tutorial/tree/master/spring-framework-mvc-basics)
21 |
22 |
--------------------------------------------------------------------------------
/account-rest-api/README.MD:
--------------------------------------------------------------------------------
1 | #
Account REST API exercise :muscle:
2 | Improve your *Spring MVC* configuration and rest mapping skills
3 | ### Task
4 | This webapp provides a **simple REST API for `Account`**. The data is stored using in-memory fake DAO. Your job is to
5 | **configure Spring MVC application** and **implement AccountRestController**. In order to complete the task, please
6 | **follow the instructions in the *todo* section**
7 |
8 | To verify your configuration, run `AccountRestControllerTest.java` :white_check_mark:
9 |
10 |
11 | ### Pre-conditions :heavy_exclamation_mark:
12 | You're supposed to be familiar with *Spring MVC*
13 |
14 | ### How to start :question:
15 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
16 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
17 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
18 |
19 | ### Related materials :information_source:
20 | * [Spring REST basics tutorial](https://github.com/bobocode-projects/spring-framework-tutorial/tree/master/rest-basics)
21 |
22 |
--------------------------------------------------------------------------------
/spring-framework-exercises-model/src/main/java/com/bobocode/model/jpa/User.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.model.jpa;
2 |
3 | import lombok.*;
4 |
5 | import javax.persistence.*;
6 | import java.time.LocalDate;
7 | import java.util.HashSet;
8 | import java.util.Set;
9 |
10 |
11 | @NoArgsConstructor
12 | @Getter
13 | @Setter
14 | @ToString
15 | @EqualsAndHashCode(of = "email")
16 | @Entity
17 | @Table(name = "user")
18 | public class User {
19 | @Id
20 | @GeneratedValue
21 | private Long id;
22 |
23 | @Column(name = "first_name")
24 | private String firstName;
25 |
26 | @Column(name = "last_name")
27 | private String lastName;
28 |
29 | @Column(name = "email")
30 | private String email;
31 |
32 | @Column(name = "birthday")
33 | private LocalDate birthday;
34 |
35 | @Column(name = "creation_date")
36 | private LocalDate creationDate;
37 |
38 | @Setter(AccessLevel.PRIVATE)
39 | @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
40 | private Set roles = new HashSet<>();
41 |
42 | public void addRole(Role role) {
43 | roles.add(role);
44 | role.setUser(this);
45 | }
46 |
47 | public void addRoles(Set roles) {
48 | this.roles.addAll(roles);
49 | roles.forEach(role -> role.setUser(this));
50 | }
51 |
52 | public void removeRole(Role role) {
53 | this.roles.remove(role);
54 | role.setUser(null);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #
Spring framework exercises
2 | The list of exercises dedicated to training your Spring framework related skills
3 |
4 | ### No pain, No gain :heavy_exclamation_mark:
5 |
6 | > Skill is only developed by hours and hours and hours of beating on your craft
7 |
8 | Working on real problems, you're focused on finding a solution. Learning new things, you're trying to understand how it works.
9 | It is important to have a different type of activities, which purpose is improving your skill
10 |
11 | ***An exercise** is a predefined task that you continuously implement to improve a certain skill* :muscle:
12 | ##
13 | * [Hello ApplicationContext](https://github.com/boy4uck/spring-framework-exercises/tree/master/hello-application-context#hello-applicationcontext-exercise-muscle)
14 | * [Hello Spring MVC](https://github.com/boy4uck/spring-framework-exercises/tree/master/hello-spring-mvc#hello-spring-mvc-exercise-muscle)
15 | * [Account JSP](https://github.com/bobocode-projects/spring-framework-exercises/tree/master/account-jsp)
16 | * [Account REST API](https://github.com/bobocode-projects/spring-framework-exercises/tree/master/account-rest-api#account-rest-api-exercise-muscle)
17 | * [Transactional User Service](https://github.com/bobocode-projects/spring-framework-exercises/tree/master/transactional-user-service#transactional-userservice-exercise-muscle)
18 |
--------------------------------------------------------------------------------
/transactional-user-service/README.MD:
--------------------------------------------------------------------------------
1 | #
Transactional UserService exercise :muscle:
2 | Improve your *Spring ORM* and *Transaction manager* configuration skills
3 | ### Task
4 | This is a *Spring* application that consists of `UserService` and `UserDao` that is implemented using *JPA*.
5 | Your job is to **configure all required beans to use *JPA*** then **configure `PlatformTransactionManager`** and
6 | **enable declarative transaction management**. In order to complete the task, please **follow the instructions
7 | in the *todo* section**
8 |
9 | To verify your configuration, run `TransactionalUserServiceTest.java` :white_check_mark:
10 |
11 |
12 | ### Pre-conditions :heavy_exclamation_mark:
13 | You're supposed to be familiar with *Spring MVC*
14 |
15 | ### How to start :question:
16 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
17 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
18 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
19 |
20 | ### Related materials :information_source:
21 | * [Spring JPA and Tx management](https://github.com/bobocode-projects/spring-framework-tutorial/tree/master/jpa-tx-management)
22 |
23 |
--------------------------------------------------------------------------------
/hello-spring-mvc/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-framework-exercises
7 | com.bobocode
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | hello-spring-mvc
13 | war
14 |
15 |
16 |
17 | org.springframework
18 | spring-webmvc
19 | 5.0.7.RELEASE
20 |
21 |
22 | javax.servlet
23 | javax.servlet-api
24 | 4.0.1
25 | provided
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | org.apache.maven.plugins
34 | maven-war-plugin
35 | 2.6
36 |
37 | false
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/account-rest-api/src/main/java/com/bobocode/dao/impl/InMemoryAccountDao.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.dao.impl;
2 |
3 | import com.bobocode.dao.AccountDao;
4 | import com.bobocode.exception.EntityNotFountException;
5 | import com.bobocode.model.Account;
6 |
7 | import java.util.ArrayList;
8 | import java.util.HashMap;
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | /**
13 | * {@link AccountDao} implementation that is based on {@link java.util.HashMap}.
14 | *
15 | * todo: 1. Configure a component with name "accountDao"
16 | */
17 | public class InMemoryAccountDao implements AccountDao {
18 | private Map accountMap = new HashMap<>();
19 | private long idSequence = 1L;
20 |
21 | @Override
22 | public List findAll() {
23 | return new ArrayList<>(accountMap.values());
24 | }
25 |
26 | @Override
27 | public Account findById(long id) {
28 | Account account = accountMap.get(id);
29 | if (account == null) {
30 | throw new EntityNotFountException(String.format("Cannot found account by id = %d", id));
31 | }
32 | return account;
33 | }
34 |
35 | @Override
36 | public Account save(Account account) {
37 | if (account.getId() == null) {
38 | account.setId(idSequence++);
39 | }
40 | accountMap.put(account.getId(), account);
41 | return account;
42 | }
43 |
44 | @Override
45 | public void remove(Account account) {
46 | accountMap.remove(account.getId());
47 | }
48 |
49 | public void clear() {
50 | accountMap.clear();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/inter-bean-dependencies/README.MD:
--------------------------------------------------------------------------------
1 | #
Inter-bean dependencies exercise :muscle:
2 | Improve your *Spring ApplicationContext* Java configuration skills
3 | ### Task
4 | The task is to **refactor Java configuration class `RootConfig`, in order to properly use Inter-bean dependencies.**
5 | Your job is to follow the instructions in the *todo* section and **implement
6 | a proper configuration.**
7 |
8 | To verify your configuration, run `RootConfigContextTest.java` and `RootConfigHacksTest.java`
9 |
10 |
11 | ### Pre-conditions :heavy_exclamation_mark:
12 | You're supposed to be familiar with *Spring IoC* and *Dependency injection* and Proxy pattern
13 |
14 | ### How to start :question:
15 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
16 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
17 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
18 |
19 | ### Related materials :information_source:
20 | * [Spring @Configuration vs @Component](https://youtu.be/KWvTgqtA_HA)
21 | * [Injecting Inter-bean Dependencies](https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/core.html#beans-java-injecting-dependencies)
22 |
23 |
--------------------------------------------------------------------------------
/account-jsp/src/test/java/com/bobocode/WelcomeControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.config.WebConfig;
5 | import org.junit.jupiter.api.BeforeEach;
6 | import org.junit.jupiter.api.Test;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
9 | import org.springframework.test.web.servlet.MockMvc;
10 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
11 | import org.springframework.web.context.WebApplicationContext;
12 |
13 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
14 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
15 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
16 |
17 | @SpringJUnitWebConfig(classes = {RootConfig.class, WebConfig.class})
18 | class WelcomeControllerTest {
19 | @Autowired
20 | private WebApplicationContext applicationContext;
21 |
22 | private MockMvc mockMvc;
23 |
24 | @BeforeEach
25 | void setup() {
26 | mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
27 | }
28 |
29 | @Test
30 | void testWelcomeUrl() throws Exception {
31 | mockMvc.perform(get("/welcome"))
32 | .andExpect(status().isOk());
33 | }
34 |
35 | @Test
36 | void testRootUrl() throws Exception {
37 | mockMvc.perform(get("/"))
38 | .andExpect(status().isOk());
39 | }
40 |
41 | @Test
42 | void testWelcomeViewName() throws Exception {
43 | mockMvc.perform(get("/"))
44 | .andExpect(view().name("welcome"));
45 | }
46 |
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/account-jsp/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-framework-exercises
7 | com.bobocode
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | account-jsp
13 | war
14 |
15 |
16 |
17 | org.springframework
18 | spring-webmvc
19 | 5.0.7.RELEASE
20 |
21 |
22 | javax.servlet
23 | javax.servlet-api
24 | 4.0.1
25 | provided
26 |
27 |
28 | com.bobocode
29 | spring-framework-exercises-util
30 | 1.0-SNAPSHOT
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.apache.maven.plugins
39 | maven-war-plugin
40 | 2.6
41 |
42 | false
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/transactional-user-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-framework-exercises
7 | com.bobocode
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | transactional-user-service
13 |
14 |
15 |
16 | org.springframework
17 | spring-orm
18 | 5.0.7.RELEASE
19 |
20 |
21 | com.h2database
22 | h2
23 | 1.4.197
24 |
25 |
26 | org.slf4j
27 | slf4j-simple
28 | 1.7.24
29 |
30 |
31 | org.hibernate.javax.persistence
32 | hibernate-jpa-2.1-api
33 | 1.0.2.Final
34 |
35 |
36 | org.hibernate
37 | hibernate-core
38 | 5.3.2.Final
39 |
40 |
41 |
42 |
43 | javax.xml.bind
44 | jaxb-api
45 | 2.2.11
46 |
47 |
48 |
49 | com.bobocode
50 | spring-framework-exercises-util
51 | 1.0-SNAPSHOT
52 |
53 |
54 |
--------------------------------------------------------------------------------
/transactional-user-service/src/main/java/com/bobocode/config/JpaConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
4 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
5 | import org.springframework.orm.jpa.JpaVendorAdapter;
6 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
7 | import org.springframework.orm.jpa.vendor.Database;
8 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
9 |
10 | import javax.sql.DataSource;
11 |
12 | /**
13 | * {@link JpaConfig} provides configuration required to create {@link javax.persistence.EntityManagerFactory}. In order
14 | * to configure {@link javax.persistence.EntityManagerFactory} Spring needs to configure two beans {@link DataSource}
15 | * and {@link JpaVendorAdapter}.
16 | *
17 | * todo: 1. Mark this class as spring config
18 | * todo: 2. Configure a bean of {@link DataSource}
19 | * todo: 3. Configure a bean of {@link JpaVendorAdapter}
20 | * todo: 4. Configure bean {@link javax.persistence.EntityManagerFactory} with name "entityManagerFactory"
21 | *
22 | */
23 | public class JpaConfig {
24 | public DataSource dataSource() {
25 | return new EmbeddedDatabaseBuilder()
26 | .setType(EmbeddedDatabaseType.H2)
27 | .build();
28 | }
29 |
30 | public JpaVendorAdapter jpaVendorAdapter() {
31 | HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
32 | adapter.setDatabase(Database.H2);
33 | adapter.setShowSql(true);
34 | adapter.setGenerateDdl(true); // this sets hibernate.hbm2ddl.auto=update (Hibernate will generate db tables)
35 | return adapter;
36 | }
37 |
38 | public LocalContainerEntityManagerFactoryBean localContainerEMF(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
39 | LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
40 | emf.setDataSource(dataSource);
41 | emf.setJpaVendorAdapter(jpaVendorAdapter);
42 | // todo: 5. Configure package "com.bobocode.model" to scan for JPA entities
43 | return emf;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/account-rest-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-framework-exercises
7 | com.bobocode
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | account-rest-api
13 | war
14 |
15 |
16 |
17 | org.springframework
18 | spring-webmvc
19 | 5.0.7.RELEASE
20 |
21 |
22 | javax.servlet
23 | javax.servlet-api
24 | 4.0.1
25 | provided
26 |
27 |
28 | com.bobocode
29 | spring-framework-exercises-util
30 | 1.0-SNAPSHOT
31 |
32 |
33 | com.fasterxml.jackson.core
34 | jackson-core
35 | 2.9.7
36 |
37 |
38 | com.fasterxml.jackson.core
39 | jackson-databind
40 | 2.9.7
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | org.apache.maven.plugins
49 | maven-war-plugin
50 | 2.6
51 |
52 | false
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/inter-bean-dependencies/src/test/java/com/bobocode/RootConfigHacksTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.dao.impl.FakeAccountDao;
5 | import com.bobocode.service.AccountService;
6 | import org.junit.jupiter.api.Test;
7 | import org.springframework.context.annotation.ComponentScan;
8 | import org.springframework.context.annotation.Import;
9 | import org.springframework.stereotype.Component;
10 | import org.springframework.stereotype.Service;
11 |
12 | import java.lang.annotation.Annotation;
13 | import java.lang.reflect.Method;
14 | import java.util.List;
15 | import java.util.stream.Collectors;
16 | import java.util.stream.Stream;
17 |
18 | import static org.assertj.core.api.Assertions.assertThat;
19 |
20 | class RootConfigHacksTest {
21 |
22 | @Test
23 | void rootConfigShouldNotUseComponentScan() {
24 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
25 |
26 | assertThat(componentScan).isNull();
27 | }
28 |
29 | @Test
30 | void rootConfigShouldNotImportOtherConfigs() {
31 | Import importAnnotation = RootConfig.class.getAnnotation(Import.class);
32 |
33 | assertThat(importAnnotation).isNull();
34 | }
35 |
36 | @Test
37 | void accountServiceBeanShouldBeConfiguredExplicitly() {
38 | Annotation[] annotations = AccountService.class.getAnnotations();
39 | List annotationClasses = Stream.of(annotations).map(Annotation::annotationType).collect(Collectors.toList());
40 |
41 | assertThat(annotationClasses).doesNotContain(Component.class, Service.class);
42 | }
43 |
44 | @Test
45 | void fakeAccountDaoBeanShouldBeConfiguredExplicitly() {
46 | Annotation[] annotations = FakeAccountDao.class.getAnnotations();
47 | List annotationClasses = Stream.of(annotations).map(Annotation::annotationType).collect(Collectors.toList());
48 |
49 | assertThat(annotationClasses).doesNotContain(Component.class, Service.class);
50 | }
51 |
52 | @Test
53 | void rootConfigShouldUseInterBeanDependencies() {
54 | Method[] methods = RootConfig.class.getDeclaredMethods();
55 |
56 | assertThat(methods).noneMatch(method -> method.getParameterCount() > 0);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/spring-framework-exercises-util/src/main/java/com/bobocode/TestDataGenerator.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.model.Account;
4 | import com.bobocode.model.Gender;
5 | import com.bobocode.model.jpa.Role;
6 | import com.bobocode.model.jpa.RoleType;
7 | import com.bobocode.model.jpa.User;
8 | import io.codearte.jfairy.Fairy;
9 | import io.codearte.jfairy.producer.person.Person;
10 |
11 | import java.math.BigDecimal;
12 | import java.time.LocalDate;
13 | import java.time.LocalDateTime;
14 | import java.util.Random;
15 | import java.util.Set;
16 | import java.util.function.Predicate;
17 | import java.util.stream.Collectors;
18 | import java.util.stream.Stream;
19 |
20 | import static java.util.stream.Collectors.toSet;
21 |
22 | public class TestDataGenerator {
23 | public Account generateAccount(){
24 | Fairy fairy = Fairy.create();
25 | Person person = fairy.person();
26 | Random random = new Random();
27 |
28 | Account fakeAccount = new Account();
29 | fakeAccount.setFirstName(person.getFirstName());
30 | fakeAccount.setLastName(person.getLastName());
31 | fakeAccount.setEmail(person.getEmail());
32 | fakeAccount.setBirthday(LocalDate.of(
33 | person.getDateOfBirth().getYear(),
34 | person.getDateOfBirth().getMonthOfYear(),
35 | person.getDateOfBirth().getDayOfMonth()));
36 | fakeAccount.setGender(Gender.valueOf(person.getSex().name()));
37 | fakeAccount.setBalance(BigDecimal.valueOf(random.nextInt(200_000)));
38 | fakeAccount.setCreationTime(LocalDateTime.now());
39 |
40 | return fakeAccount;
41 | }
42 |
43 | public User generateUser() {
44 | User user = generateUserOnly();
45 | user.addRoles(generateRoleSet());
46 | return user;
47 | }
48 |
49 | public User generateUser(RoleType... roleTypes) {
50 | Set roles = Stream.of(roleTypes).map(Role::valueOf).collect(Collectors.toSet());
51 | User user = generateUserOnly();
52 | user.addRoles(roles);
53 | return user;
54 | }
55 |
56 | private User generateUserOnly() {
57 | Fairy fairy = Fairy.create();
58 | Person person = fairy.person();
59 |
60 | User user = new User();
61 | user.setFirstName(person.getFirstName());
62 | user.setLastName(person.getLastName());
63 | user.setEmail(person.getEmail());
64 | user.setBirthday(LocalDate.of(
65 | person.getDateOfBirth().getYear(),
66 | person.getDateOfBirth().getMonthOfYear(),
67 | person.getDateOfBirth().getDayOfMonth()));
68 | user.setCreationDate(LocalDate.now());
69 | return user;
70 | }
71 |
72 | public static Set generateRoleSet() {
73 | Random random = new Random();
74 | Predicate randomPredicate = i -> random.nextBoolean();
75 |
76 | return Stream.of(RoleType.values())
77 | .filter(randomPredicate)
78 | .map(Role::valueOf)
79 | .collect(toSet());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/account-jsp/src/test/java/com/bobocode/AccountControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.config.WebConfig;
5 | import com.bobocode.web.controller.AccountController;
6 | import org.junit.jupiter.api.BeforeEach;
7 | import org.junit.jupiter.api.Test;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.stereotype.Controller;
10 | import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
11 | import org.springframework.test.web.servlet.MockMvc;
12 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
13 | import org.springframework.web.bind.annotation.RequestMapping;
14 | import org.springframework.web.context.WebApplicationContext;
15 |
16 | import static org.hamcrest.MatcherAssert.assertThat;
17 | import static org.hamcrest.Matchers.arrayContaining;
18 | import static org.hamcrest.Matchers.arrayWithSize;
19 | import static org.hamcrest.Matchers.hasSize;
20 | import static org.hamcrest.core.IsNull.notNullValue;
21 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
22 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
23 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
24 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
25 |
26 | @SpringJUnitWebConfig(classes = {RootConfig.class, WebConfig.class})
27 | class AccountControllerTest {
28 | @Autowired
29 | private WebApplicationContext applicationContext;
30 |
31 | private MockMvc mockMvc;
32 |
33 | @BeforeEach
34 | void setup() {
35 | mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
36 | }
37 |
38 | @Test
39 | void testAccountControllerAnnotation() {
40 | Controller controller = AccountController.class.getAnnotation(Controller.class);
41 |
42 | assertThat(controller, notNullValue());
43 | }
44 |
45 | @Test
46 | void testAccountControllerRequestMapping() {
47 | RequestMapping requestMapping = AccountController.class.getAnnotation(RequestMapping.class);
48 |
49 | assertThat(requestMapping, notNullValue());
50 | assertThat(requestMapping.value(), arrayWithSize(1));
51 | assertThat(requestMapping.value(), arrayContaining("/accounts"));
52 | }
53 |
54 | @Test
55 | void testGetAccountsResponseStatusCode() throws Exception {
56 | mockMvc.perform(get("/accounts")).andExpect(status().isOk());
57 | }
58 |
59 | @Test
60 | void testGetAccountsViewName() throws Exception {
61 | mockMvc.perform(get("/accounts"))
62 | .andExpect(view().name("accounts"));
63 | }
64 |
65 | @Test
66 | void testGetAccountsModelContainsAccountList() throws Exception {
67 | mockMvc.perform(get("/accounts"))
68 | .andExpect(model().attributeExists("accountList"));
69 | }
70 |
71 | @Test
72 | void testAccountsResponseDefaultListSize() throws Exception {
73 | mockMvc.perform(get("/accounts"))
74 | .andExpect(model().attribute("accountList", hasSize(10)));
75 | }
76 |
77 | @Test
78 | void testAccountsResponseListSize() throws Exception {
79 | mockMvc.perform(get("/accounts").param("size", "20"))
80 | .andExpect(model().attribute("accountList", hasSize(20)));
81 | }
82 |
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.bobocode
8 | spring-framework-exercises
9 | 1.0-SNAPSHOT
10 |
11 | hello-application-context
12 | spring-framework-exercises-model
13 | spring-framework-exercises-util
14 | hello-spring-mvc
15 | account-jsp
16 | account-rest-api
17 | transactional-user-service
18 | inter-bean-dependencies
19 |
20 | pom
21 |
22 |
23 | 11
24 | 11
25 |
26 |
27 |
28 |
29 | org.projectlombok
30 | lombok
31 | 1.18.0
32 |
33 |
34 | org.springframework
35 | spring-context
36 | 5.0.7.RELEASE
37 |
38 |
39 | org.springframework
40 | spring-test
41 | 5.0.7.RELEASE
42 |
43 |
44 | javax.annotation
45 | javax.annotation-api
46 | 1.3.2
47 |
48 |
49 | org.junit.jupiter
50 | junit-jupiter-engine
51 | 5.2.0
52 | test
53 |
54 |
55 | org.junit.platform
56 | junit-platform-launcher
57 | 1.3.1
58 | test
59 |
60 |
61 | com.jayway.jsonpath
62 | json-path
63 | 2.3.0
64 |
65 |
66 | com.jayway.jsonpath
67 | json-path-assert
68 | 2.3.0
69 |
70 |
71 |
72 | org.hamcrest
73 | hamcrest-all
74 | 1.3
75 | test
76 |
77 |
78 | org.slf4j
79 | slf4j-simple
80 | 1.7.24
81 |
82 |
83 | org.assertj
84 | assertj-core
85 | 3.13.2
86 | test
87 |
88 |
89 |
90 |
91 |
92 |
93 | org.apache.maven.plugins
94 | maven-surefire-plugin
95 | 2.22.0
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/test/java/com/bobocode/WelcomeWebAppTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.config.WebConfig;
5 | import com.bobocode.web.controller.WelcomeController;
6 | import org.junit.jupiter.api.Test;
7 | import org.springframework.context.annotation.ComponentScan;
8 | import org.springframework.context.annotation.ComponentScan.Filter;
9 | import org.springframework.context.annotation.Configuration;
10 | import org.springframework.context.annotation.FilterType;
11 | import org.springframework.stereotype.Controller;
12 | import org.springframework.web.bind.annotation.GetMapping;
13 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
14 |
15 | import java.util.List;
16 | import java.util.stream.Collectors;
17 | import java.util.stream.Stream;
18 |
19 | import static org.hamcrest.MatcherAssert.assertThat;
20 | import static org.hamcrest.Matchers.arrayContaining;
21 | import static org.hamcrest.Matchers.arrayWithSize;
22 | import static org.hamcrest.Matchers.containsInAnyOrder;
23 | import static org.hamcrest.Matchers.equalTo;
24 | import static org.hamcrest.Matchers.notNullValue;
25 |
26 | class WelcomeWebAppTest {
27 |
28 | @Test
29 | void testDispatcherServletMapping() {
30 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
31 |
32 | assertThat(webAppInitializerWrapper.getServletMappings(), arrayContaining("/"));
33 | }
34 |
35 | @Test
36 | void testInitializerRootConfigClasses() {
37 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
38 |
39 | assertThat(webAppInitializerWrapper.getRootConfigClasses(), arrayContaining(RootConfig.class));
40 | }
41 |
42 | @Test
43 | void testInitializerWebConfigClasses() {
44 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
45 |
46 | assertThat(webAppInitializerWrapper.getServletConfigClasses(), arrayContaining(WebConfig.class));
47 | }
48 |
49 | @Test
50 | void testRootConfigClassIsMarkedAsConfiguration() {
51 | Configuration configuration = RootConfig.class.getAnnotation(Configuration.class);
52 |
53 | assertThat(configuration, notNullValue());
54 | }
55 |
56 | @Test
57 | void testRootConfigClassEnablesComponentScan() {
58 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
59 |
60 | assertThat(componentScan, notNullValue());
61 | }
62 |
63 | @Test
64 | void testRootConfigComponentScanPackages() {
65 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
66 | String[] packages = componentScan.basePackages();
67 | if (packages.length == 0) {
68 | packages = componentScan.value();
69 | }
70 |
71 | assertThat(packages, arrayContaining("com.bobocode"));
72 | }
73 |
74 | @Test
75 | void testRootConfigComponentScanFilters() {
76 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
77 | Filter[] filters = componentScan.excludeFilters();
78 | List filteredClasses = getFilteredClasses(filters);
79 |
80 | assertThat(filters, arrayWithSize(2));
81 | assertThat(filters[0].type(), equalTo(FilterType.ANNOTATION));
82 | assertThat(filters[1].type(), equalTo(FilterType.ANNOTATION));
83 | assertThat(filteredClasses, containsInAnyOrder(EnableWebMvc.class, Controller.class));
84 | }
85 |
86 | private List getFilteredClasses(Filter[] filters) {
87 | return Stream.of(filters).flatMap(filter -> Stream.of(filter.value())).collect(Collectors.toList());
88 | }
89 |
90 | @Test
91 | void testWebConfigIsMarkedAsConfiguration() {
92 | Configuration configuration = WebConfig.class.getAnnotation(Configuration.class);
93 |
94 | assertThat(configuration, notNullValue());
95 | }
96 |
97 | @Test
98 | void testWebConfigEnablesComponentScan() {
99 | ComponentScan componentScan = WebConfig.class.getAnnotation(ComponentScan.class);
100 |
101 | assertThat(componentScan, notNullValue());
102 | }
103 |
104 | @Test
105 | void testWebConfigComponentScanPackages() {
106 | ComponentScan componentScan = WebConfig.class.getAnnotation(ComponentScan.class);
107 |
108 | assertThat(componentScan.basePackages(), arrayContaining("com.bobocode.web"));
109 | }
110 |
111 | @Test
112 | void testWebConfigEnablesWebMvc() {
113 | EnableWebMvc enableWebMvc = WebConfig.class.getAnnotation(EnableWebMvc.class);
114 |
115 | assertThat(enableWebMvc, notNullValue());
116 | }
117 |
118 | @Test
119 | void testWelcomeControllerIsMarkedAsController() {
120 | Controller controller = WelcomeController.class.getAnnotation(Controller.class);
121 |
122 | assertThat(controller, notNullValue());
123 | }
124 |
125 | @Test
126 | void testWelcomeControllerMethodIsMarkedAsGetMethod() throws NoSuchMethodException {
127 | GetMapping getMapping = WelcomeController.class.getDeclaredMethod("welcome").getAnnotation(GetMapping.class);
128 |
129 | assertThat(getMapping, notNullValue());
130 | }
131 |
132 | @Test
133 | void testWelcomeControllerMethodMapping() throws NoSuchMethodException {
134 | GetMapping getMapping = WelcomeController.class.getDeclaredMethod("welcome").getAnnotation(GetMapping.class);
135 |
136 | assertThat(getMapping.value(), arrayContaining("/welcome"));
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/account-jsp/src/test/java/com/bobocode/WebAppConfigTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.config.WebConfig;
5 | import com.bobocode.util.WebAppInitializerWrapper;
6 | import org.junit.jupiter.api.Test;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.context.ApplicationContext;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.ComponentScan;
11 | import org.springframework.context.annotation.ComponentScan.Filter;
12 | import org.springframework.context.annotation.Configuration;
13 | import org.springframework.context.annotation.FilterType;
14 | import org.springframework.stereotype.Controller;
15 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
16 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
17 |
18 | import java.lang.reflect.Method;
19 | import java.util.List;
20 | import java.util.stream.Collectors;
21 | import java.util.stream.Stream;
22 |
23 | import static org.hamcrest.MatcherAssert.assertThat;
24 | import static org.hamcrest.Matchers.arrayContaining;
25 | import static org.hamcrest.Matchers.arrayWithSize;
26 | import static org.hamcrest.Matchers.containsInAnyOrder;
27 | import static org.hamcrest.Matchers.equalTo;
28 | import static org.hamcrest.Matchers.notNullValue;
29 |
30 | @SpringJUnitConfig(classes = RootConfig.class)
31 | class WebAppConfigTest {
32 |
33 | @Autowired
34 | private ApplicationContext applicationContext;
35 |
36 | @Test
37 | void testDispatcherServletMapping() {
38 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
39 |
40 | assertThat(webAppInitializerWrapper.getServletMappings(), arrayContaining("/"));
41 | }
42 |
43 | @Test
44 | void testInitializerRootConfigClasses() {
45 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
46 |
47 | assertThat(webAppInitializerWrapper.getRootConfigClasses(), arrayContaining(RootConfig.class));
48 | }
49 |
50 | @Test
51 | void testInitializerWebConfigClasses() {
52 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
53 |
54 | assertThat(webAppInitializerWrapper.getServletConfigClasses(), arrayContaining(WebConfig.class));
55 | }
56 |
57 | @Test
58 | void testRootConfigClassIsMarkedAsConfiguration() {
59 | Configuration configuration = RootConfig.class.getAnnotation(Configuration.class);
60 |
61 | assertThat(configuration, notNullValue());
62 | }
63 |
64 | @Test
65 | void testRootConfigClassEnablesComponentScan() {
66 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
67 |
68 | assertThat(componentScan, notNullValue());
69 | }
70 |
71 | @Test
72 | void testRootConfigComponentScanPackages() {
73 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
74 |
75 | assertThat(componentScan.basePackages(), arrayContaining("com.bobocode"));
76 | }
77 |
78 | @Test
79 | void testRootConfigComponentScanFilters() {
80 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
81 | Filter[] filters = componentScan.excludeFilters();
82 | List filteredClasses = getFilteredClasses(filters);
83 |
84 | assertThat(filters, arrayWithSize(2));
85 | assertThat(filters[0].type(), equalTo(FilterType.ANNOTATION));
86 | assertThat(filters[1].type(), equalTo(FilterType.ANNOTATION));
87 | assertThat(filteredClasses, containsInAnyOrder(EnableWebMvc.class, Controller.class));
88 | }
89 |
90 | private List getFilteredClasses(Filter[] filters) {
91 | return Stream.of(filters).flatMap(filter -> Stream.of(filter.value())).collect(Collectors.toList());
92 | }
93 |
94 | @Test
95 | void testWebConfigIsMarkedAsConfiguration() {
96 | Configuration configuration = WebConfig.class.getAnnotation(Configuration.class);
97 |
98 | assertThat(configuration, notNullValue());
99 | }
100 |
101 | @Test
102 | void testWebConfigEnablesComponentScan() {
103 | ComponentScan componentScan = WebConfig.class.getAnnotation(ComponentScan.class);
104 |
105 | assertThat(componentScan, notNullValue());
106 | }
107 |
108 | @Test
109 | void testWebConfigComponentScanPackages() {
110 | ComponentScan componentScan = WebConfig.class.getAnnotation(ComponentScan.class);
111 |
112 | assertThat(componentScan.value(), arrayContaining("com.bobocode.web"));
113 | }
114 |
115 | @Test
116 | void testWebConfigEnablesWebMvc() {
117 | EnableWebMvc enableWebMvc = WebConfig.class.getAnnotation(EnableWebMvc.class);
118 |
119 | assertThat(enableWebMvc, notNullValue());
120 | }
121 |
122 | @Test
123 | void testDataGeneratorBeanName() {
124 | TestDataGenerator dataGenerator = applicationContext.getBean("dataGenerator", TestDataGenerator.class);
125 |
126 | assertThat(dataGenerator, notNullValue());
127 | }
128 |
129 | @Test
130 | void testDataGeneratorBeanNameIsNotSpecifiedExplicitly() {
131 | Method[] methods = RootConfig.class.getMethods();
132 | Method testDataGeneratorBeanMethod = findTestDataGeneratorBeanMethod(methods);
133 | Bean bean = testDataGeneratorBeanMethod.getDeclaredAnnotation(Bean.class);
134 |
135 | assertThat(bean.name(), arrayWithSize(0));
136 | assertThat(bean.value(), arrayWithSize(0));
137 | }
138 |
139 | private Method findTestDataGeneratorBeanMethod(Method[] methods) {
140 | for (Method method : methods) {
141 | if (method.getReturnType().equals(TestDataGenerator.class)
142 | && method.getDeclaredAnnotation(Bean.class) != null) {
143 | return method;
144 | }
145 | }
146 | return null;
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/account-rest-api/src/test/java/com/bobocode/AccountRestControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.config.WebConfig;
5 | import com.bobocode.dao.impl.InMemoryAccountDao;
6 | import com.bobocode.model.Account;
7 | import com.bobocode.web.controller.AccountRestController;
8 | import org.junit.jupiter.api.BeforeEach;
9 | import org.junit.jupiter.api.Test;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.http.MediaType;
12 | import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
13 | import org.springframework.test.web.servlet.MockMvc;
14 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
15 | import org.springframework.web.bind.annotation.RequestMapping;
16 | import org.springframework.web.bind.annotation.RestController;
17 | import org.springframework.web.context.WebApplicationContext;
18 |
19 | import static org.hamcrest.MatcherAssert.assertThat;
20 | import static org.hamcrest.Matchers.arrayContaining;
21 | import static org.hamcrest.Matchers.arrayWithSize;
22 | import static org.hamcrest.Matchers.hasItems;
23 | import static org.hamcrest.core.IsNull.notNullValue;
24 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
25 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
26 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
27 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
30 |
31 | @SpringJUnitWebConfig(classes = {RootConfig.class, WebConfig.class})
32 | class AccountRestControllerTest {
33 | @Autowired
34 | private WebApplicationContext applicationContext;
35 |
36 | @Autowired
37 | private InMemoryAccountDao accountDao;
38 |
39 | private MockMvc mockMvc;
40 |
41 | @BeforeEach
42 | void setup() {
43 | mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
44 | accountDao.clear();
45 | }
46 |
47 | @Test
48 | void testAccountRestControllerAnnotation() {
49 | RestController restController = AccountRestController.class.getAnnotation(RestController.class);
50 |
51 | assertThat(restController, notNullValue());
52 | }
53 |
54 | @Test
55 | void testAccountRestControllerRequestMapping() {
56 | RequestMapping requestMapping = AccountRestController.class.getAnnotation(RequestMapping.class);
57 |
58 | assertThat(requestMapping, notNullValue());
59 | assertThat(requestMapping.value(), arrayWithSize(1));
60 | assertThat(requestMapping.value(), arrayContaining("/accounts"));
61 | }
62 |
63 | @Test
64 | void testHttpStatusCodeOnCreate() throws Exception {
65 | mockMvc.perform(
66 | post("/accounts")
67 | .contentType(MediaType.APPLICATION_JSON)
68 | .content("{\"firstName\":\"Johnny\", \"lastName\":\"Boy\", \"email\":\"jboy@gmail.com\"}"))
69 | .andExpect(status().isCreated());
70 | }
71 |
72 | @Test
73 | void testCreateAccountReturnsAssignedId() throws Exception {
74 | mockMvc.perform(
75 | post("/accounts")
76 | .contentType(MediaType.APPLICATION_JSON)
77 | .content("{\"firstName\":\"Johnny\", \"lastName\":\"Boy\", \"email\":\"jboy@gmail.com\"}"))
78 | .andExpect(jsonPath("$.id").value(1L));
79 | }
80 |
81 | @Test
82 | void testGetAccountsResponseStatusCode() throws Exception {
83 | mockMvc.perform(get("/accounts").accept(MediaType.APPLICATION_JSON_UTF8))
84 | .andExpect(status().isOk());
85 | }
86 |
87 | @Test
88 | void testGetAllAccounts() throws Exception {
89 | Account account1 = create("Johnny", "Boy", "jboy@gmail.com");
90 | Account account2 = create("Okko", "Bay", "obay@gmail.com");
91 | accountDao.save(account1);
92 | accountDao.save(account2);
93 |
94 | mockMvc.perform(get("/accounts"))
95 | .andExpect(status().isOk())
96 | .andExpect(jsonPath("$.[*].email").value(hasItems("jboy@gmail.com", "obay@gmail.com")));
97 | }
98 |
99 | private Account create(String firstName, String lastName, String email) {
100 | Account account = new Account();
101 | account.setFirstName(firstName);
102 | account.setLastName(lastName);
103 | account.setEmail(email);
104 | return account;
105 | }
106 |
107 | @Test
108 | void testGetById() throws Exception {
109 | Account account = create("Johnny", "Boy", "jboy@gmail.com");
110 | accountDao.save(account);
111 |
112 | mockMvc.perform(get(String.format("/accounts/%d", account.getId())))
113 | .andExpect(status().isOk())
114 | .andExpect(jsonPath("$.id").value(account.getId()))
115 | .andExpect(jsonPath("$.email").value("jboy@gmail.com"))
116 | .andExpect(jsonPath("$.firstName").value("Johnny"))
117 | .andExpect(jsonPath("$.lastName").value("Boy"));
118 | }
119 |
120 | @Test
121 | void testRemoveAccount() throws Exception {
122 | Account account = create("Johnny", "Boy", "jboy@gmail.com");
123 | accountDao.save(account);
124 |
125 | mockMvc.perform(delete(String.format("/accounts/%d", account.getId())))
126 | .andExpect(status().isNoContent());
127 | }
128 |
129 | @Test
130 | void testUpdateAccount() throws Exception {
131 | Account account = create("Johnny", "Boy", "jboy@gmail.com");
132 | accountDao.save(account);
133 |
134 | mockMvc.perform(put(String.format("/accounts/%d", account.getId())).contentType(MediaType.APPLICATION_JSON)
135 | .content(String.format("{\"id\":\"%d\", \"firstName\":\"Johnny\", \"lastName\":\"Boy\", \"email\":\"johnny.boy@gmail.com\"}", account.getId())))
136 | .andExpect(status().isNoContent());
137 | }
138 |
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/transactional-user-service/src/test/java/com/bobocode/TransactionalUserServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.dao.UserDao;
5 | import com.bobocode.dao.impl.JpaUserDao;
6 | import com.bobocode.model.jpa.Role;
7 | import com.bobocode.model.jpa.RoleType;
8 | import com.bobocode.model.jpa.User;
9 | import com.bobocode.service.UserService;
10 | import org.junit.jupiter.api.Test;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.context.ApplicationContext;
13 | import org.springframework.context.annotation.Bean;
14 | import org.springframework.context.annotation.Configuration;
15 | import org.springframework.stereotype.Repository;
16 | import org.springframework.stereotype.Service;
17 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
18 | import org.springframework.transaction.PlatformTransactionManager;
19 | import org.springframework.transaction.annotation.Transactional;
20 |
21 | import javax.persistence.EntityManagerFactory;
22 | import java.util.List;
23 | import java.util.stream.Stream;
24 |
25 | import static java.util.stream.Collectors.toList;
26 | import static org.hamcrest.MatcherAssert.assertThat;
27 | import static org.hamcrest.Matchers.contains;
28 | import static org.hamcrest.Matchers.containsInAnyOrder;
29 | import static org.hamcrest.Matchers.hasProperty;
30 | import static org.hamcrest.Matchers.is;
31 | import static org.hamcrest.core.IsCollectionContaining.hasItem;
32 | import static org.hamcrest.core.IsNull.notNullValue;
33 |
34 | @SpringJUnitConfig(RootConfig.class)
35 | @Transactional
36 | class TransactionalUserServiceTest {
37 | @Configuration
38 | static class TestConfig {
39 | @Bean
40 | TestDataGenerator dataGenerator() {
41 | return new TestDataGenerator();
42 | }
43 | }
44 |
45 | @Autowired
46 | private ApplicationContext applicationContext;
47 | @Autowired
48 | private UserService userService;
49 | @Autowired
50 | private UserDao userDao;
51 | @Autowired
52 | private TestDataGenerator dataGenerator;
53 |
54 | @Test
55 | void testTxManagerBeanName() {
56 | PlatformTransactionManager transactionManager = applicationContext.getBean(PlatformTransactionManager.class, "transactionManager");
57 |
58 | assertThat(transactionManager, notNullValue());
59 | }
60 |
61 | @Test
62 | void testUserDaoBeanName() {
63 | UserDao userDao = applicationContext.getBean(UserDao.class, "userDao");
64 |
65 | assertThat(userDao, notNullValue());
66 | }
67 |
68 | @Test
69 | void testEntityManagerFactoryBeanName() {
70 | EntityManagerFactory entityManagerFactory = applicationContext.getBean(EntityManagerFactory.class, "entityManagerFactory");
71 |
72 | assertThat(entityManagerFactory, notNullValue());
73 | }
74 |
75 | @Test
76 | void testUserServiceIsMarkedAsService() {
77 | Service service = UserService.class.getAnnotation(Service.class);
78 |
79 | assertThat(service, notNullValue());
80 | }
81 |
82 | @Test
83 | void testUserDaoIsMarkedAsRepository() {
84 | Repository repository = JpaUserDao.class.getAnnotation(Repository.class);
85 |
86 | assertThat(repository, notNullValue());
87 | }
88 |
89 | @Test
90 | void testUserServiceIsTransactional() {
91 | Transactional transactional = UserService.class.getAnnotation(Transactional.class);
92 |
93 | assertThat(transactional, notNullValue());
94 | }
95 |
96 | @Test
97 | void testUserServiceGetAllIsReadOnly() throws NoSuchMethodException {
98 | Transactional transactional = UserService.class.getDeclaredMethod("getAll").getAnnotation(Transactional.class);
99 |
100 | assertThat(transactional.readOnly(), is(true));
101 | }
102 |
103 | @Test
104 | void testUserServiceGetAllAdminsIsReadOnly() throws NoSuchMethodException {
105 | Transactional transactional = UserService.class.getDeclaredMethod("getAllAdmins").getAnnotation(Transactional.class);
106 |
107 | assertThat(transactional.readOnly(), is(true));
108 | }
109 |
110 | @Test
111 | void testUserDaoIsTransactional() {
112 | Transactional transactional = JpaUserDao.class.getAnnotation(Transactional.class);
113 |
114 | assertThat(transactional, notNullValue());
115 | }
116 |
117 | @Test
118 | void testSaveUser() {
119 | User user = dataGenerator.generateUser();
120 | userService.save(user);
121 |
122 | assertThat(userDao.findAll(), hasItem(user));
123 | }
124 |
125 | @Test
126 | void testGetAllUsers() {
127 | List userList = generateUserList(10);
128 | userList.forEach(userService::save);
129 |
130 | List users = userService.getAll();
131 | assertThat(users, containsInAnyOrder(userList.toArray()));
132 | }
133 |
134 | private List generateUserList(int size) {
135 | return Stream.generate(dataGenerator::generateUser)
136 | .limit(size)
137 | .collect(toList());
138 | }
139 |
140 | @Test
141 | void testGetAllAdmins() {
142 | List userList = generateUserList(20);
143 | userList.forEach(userService::save);
144 |
145 | List admins = userService.getAllAdmins();
146 |
147 | assertThat(admins, containsInAnyOrder(findAdmins(userList).toArray()));
148 | }
149 |
150 | private List findAdmins(List users) {
151 | return users.stream()
152 | .filter(user -> user.getRoles().stream()
153 | .map(Role::getRoleType)
154 | .anyMatch(roleType -> roleType.equals(RoleType.ADMIN)))
155 | .collect(toList());
156 |
157 | }
158 |
159 | @Test
160 | void testAddNewRole() {
161 | User user = dataGenerator.generateUser(RoleType.USER);
162 | userService.save(user);
163 |
164 | userService.addRole(user.getId(), RoleType.ADMIN);
165 |
166 | User loadedUser = userDao.findById(user.getId());
167 | assertThat(loadedUser.getRoles(), contains(
168 | hasProperty("roleType", is(RoleType.USER)),
169 | hasProperty("roleType", is(RoleType.ADMIN)))
170 | );
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/hello-application-context/src/test/java/com/bobocode/AppConfigTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.AppConfig;
4 | import com.bobocode.dao.AccountDao;
5 | import com.bobocode.dao.FakeAccountDao;
6 | import com.bobocode.model.Account;
7 | import com.bobocode.service.AccountService;
8 | import org.junit.jupiter.api.Test;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.context.ApplicationContext;
11 | import org.springframework.context.annotation.Bean;
12 | import org.springframework.context.annotation.ComponentScan;
13 | import org.springframework.context.annotation.Configuration;
14 | import org.springframework.stereotype.Component;
15 | import org.springframework.stereotype.Service;
16 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.lang.reflect.Method;
20 | import java.util.Comparator;
21 | import java.util.Map;
22 |
23 | import static org.hamcrest.MatcherAssert.assertThat;
24 | import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
25 | import static org.hamcrest.Matchers.arrayWithSize;
26 | import static org.hamcrest.Matchers.equalTo;
27 | import static org.hamcrest.Matchers.hasItem;
28 | import static org.hamcrest.Matchers.is;
29 | import static org.hamcrest.Matchers.notNullValue;
30 |
31 |
32 | @SpringJUnitConfig
33 | class AppConfigTest {
34 | @Configuration
35 | @ComponentScan(basePackages = "com.bobocode")
36 | static class TestConfig {
37 | }
38 |
39 | @Autowired
40 | private ApplicationContext applicationContext;
41 |
42 | @Autowired
43 | private AccountService accountService;
44 |
45 | @Autowired
46 | private AccountDao accountDao;
47 |
48 | @Test
49 | void testConfigClassIsMarkedAsConfiguration() {
50 | Configuration configuration = AppConfig.class.getAnnotation(Configuration.class);
51 |
52 | assertThat(configuration, notNullValue());
53 | }
54 |
55 | @Test
56 | void testComponentScanIsEnabled() {
57 | ComponentScan componentScan = AppConfig.class.getAnnotation(ComponentScan.class);
58 |
59 | assertThat(componentScan, notNullValue());
60 | }
61 |
62 | @Test
63 | void testComponentScanPackagesAreSpecified() {
64 | ComponentScan componentScan = AppConfig.class.getAnnotation(ComponentScan.class);
65 | String[] packages = componentScan.basePackages();
66 | if (packages.length == 0) {
67 | packages = componentScan.value();
68 | }
69 | assertThat(packages, arrayContainingInAnyOrder("com.bobocode.dao", "com.bobocode.service"));
70 | }
71 |
72 | @Test
73 | void testDataGeneratorHasOnlyOneBean() {
74 | Map testDataGeneratorMap = applicationContext.getBeansOfType(TestDataGenerator.class);
75 |
76 | assertThat(testDataGeneratorMap.size(), is(1));
77 | }
78 |
79 | @Test
80 | void testDataGeneratorBeanIsConfiguredExplicitly() {
81 | Method[] methods = AppConfig.class.getMethods();
82 | Method testDataGeneratorBeanMethod = findTestDataGeneratorBeanMethod(methods);
83 |
84 |
85 | assertThat(testDataGeneratorBeanMethod, notNullValue());
86 | }
87 |
88 | @Test
89 | void testDataGeneratorBeanName() {
90 | Map dataGeneratorBeanMap = applicationContext.getBeansOfType(TestDataGenerator.class);
91 |
92 | assertThat(dataGeneratorBeanMap.keySet(), hasItem("dataGenerator"));
93 | }
94 |
95 | @Test
96 | void testDataGeneratorBeanNameIsNotSpecifiedExplicitly() {
97 | Method[] methods = AppConfig.class.getMethods();
98 | Method testDataGeneratorBeanMethod = findTestDataGeneratorBeanMethod(methods);
99 | Bean bean = testDataGeneratorBeanMethod.getDeclaredAnnotation(Bean.class);
100 |
101 | assertThat(bean.name(), arrayWithSize(0));
102 | assertThat(bean.value(), arrayWithSize(0));
103 | }
104 |
105 | private Method findTestDataGeneratorBeanMethod(Method[] methods) {
106 | for (Method method : methods) {
107 | if (method.getReturnType().equals(TestDataGenerator.class)
108 | && method.getDeclaredAnnotation(Bean.class) != null) {
109 | return method;
110 | }
111 | }
112 | return null;
113 | }
114 |
115 | @Test
116 | void testFakeAccountDaoIsConfiguredAsComponent() {
117 | Component component = FakeAccountDao.class.getAnnotation(Component.class);
118 |
119 | assertThat(component, notNullValue());
120 | }
121 |
122 | @Test
123 | void testAccountDaoHasOnlyOneBean() {
124 | Map accountDaoBeanMap = applicationContext.getBeansOfType(AccountDao.class);
125 |
126 | assertThat(accountDaoBeanMap.size(), is(1));
127 | }
128 |
129 | @Test
130 | void testAccountDaoBeanName() {
131 | Map accountDaoBeanMap = applicationContext.getBeansOfType(AccountDao.class);
132 |
133 | assertThat(accountDaoBeanMap.keySet(), hasItem("accountDao"));
134 | }
135 |
136 | @Test
137 | void testAccountDaoConstructorIsMarkedWithAutowired() throws NoSuchMethodException {
138 | Autowired autowired = FakeAccountDao.class.getConstructor(TestDataGenerator.class).getAnnotation(Autowired.class);
139 |
140 | assertThat(autowired, notNullValue());
141 | }
142 |
143 | @Test
144 | void testAccountServiceHasOnlyOneBean() {
145 | Map accountServiceMap = applicationContext.getBeansOfType(AccountService.class);
146 |
147 | assertThat(accountServiceMap.size(), is(1));
148 | }
149 |
150 | @Test
151 | void testAccountServiceIsConfiguredAsService() {
152 | Service service = AccountService.class.getAnnotation(Service.class);
153 |
154 | assertThat(service, notNullValue());
155 | }
156 |
157 | @Test
158 | void testAccountServiceBeanName() {
159 | Map accountServiceMap = applicationContext.getBeansOfType(AccountService.class);
160 |
161 | assertThat(accountServiceMap.keySet(), hasItem("accountService"));
162 | }
163 |
164 | @Test
165 | void testAccountServiceBeanNameIsNotSpecifiedExplicitly() {
166 | Service service = AccountService.class.getAnnotation(Service.class);
167 |
168 | assertThat(service.value(), equalTo(""));
169 | }
170 |
171 | @Test
172 | void testAccountServiceDoesNotUseAutowired() throws NoSuchMethodException {
173 | Annotation[] annotations = AccountService.class.getConstructor(AccountDao.class).getDeclaredAnnotations();
174 |
175 | assertThat(annotations, arrayWithSize(0));
176 | }
177 |
178 | @Test
179 | void testFindRichestAccount() {
180 | Account richestAccount = accountService.findRichestAccount();
181 |
182 | Account actualRichestAccount = accountDao.findAll().stream().max(Comparator.comparing(Account::getBalance)).get();
183 |
184 | assertThat(richestAccount, equalTo(actualRichestAccount));
185 | }
186 |
187 |
188 | }
189 |
--------------------------------------------------------------------------------