Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
ÔÚDockerÖÐÔËÐÐSpring BootµÄ¸ß¼¶¹¦ÄܲâÊÔ
 
  2838  次浏览      27
 2019-1-30
 

 

±à¼­ÍƼö:

±¾ÎÄÀ´×ÔÓÚÍøÂ磬ÎÄÕ½éÉÜÁËÈçºÎʹÓÃSpring Boot½øÐй¦ÄܲâÊÔµÄһЩ×î¼Ñʵ¼ùµÄÏà¹ØÄÚÈÝ¡£

¸ÅÀÀ

±¾ÎÄÖØµã½éÉÜÈçºÎʹÓÃSpring Boot½øÐй¦ÄܲâÊÔµÄһЩ×î¼Ñʵ¼ù¡£ÎÒÃǽ«ÑÝʾÈçºÎÔÚ²»ÉèÖÃÄ£Äâ»·¾³µÄÇé¿öϽ«·þÎñ×÷ΪºÚºÐ²âÊԵĸ߼¶·½·¨¡£

±¾ÎÄÊÇÎÒ֮ǰÕâÆªÎÄÕ Native Integration Testing in Spring Boot µÄºóÐø¡£

Òò´ËÎÒ½«²Î¿¼ÉÏһƪÎÄÕÂÀ´½éÉÜÕâÁ½ÖÖ²âÊÔ·½·¨µÄÇø±ð¡£

ÎÒ½¨ÒéÄãÔÚÔĶÁÕâÆªÎÄÕÂ֮ǰÏÈÁ˽âÉÏһƪÎÄÕ¡£

ÀíÂÛ

ÈÃÎÒÃÇ´Ó¹¦ÄܲâÊԵ͍Ò忪ʼ£¨À´×ÔÓÚTechopedia£©£º

¹¦ÄܲâÊÔÊÇÔÚÈí¼þ¿ª·¢¹ý³ÌÖÐʹÓõÄÈí¼þ²âÊÔÁ÷³Ì£¬Í¨¹ý²âÊÔÀ´È·±£Èí¼þ·ûºÏËùÓеÄÔ¤ÆÚÐèÇó¡£

¹¦ÄܲâÊÔÒ²ÊÇÒ»ÖÖ¼ì²éÈí¼þµÄ·½·¨£¬Í¨¹ýÕâÖÖ·½·¨À´È·±£Ëü¾ßÓÐÖ¸¶¨µÄËùÓбØÐ빦ÄÜ¡£

ËäÈ»Õâ¸ö½âÊÍ¿´ÆðÀ´ÓеãÈÃÈËÃԻ󣬵«²»ÐèÒªµ£ÐÄ¡ª¡ª½ÓÏÂÀ´µÄ¶¨ÒåÌṩÁ˸ü½øÒ»²½µÄ½âÊÍ£¨À´×ÔÓÚTechopedia£©£º

¹¦ÄܲâÊÔÖ÷ÒªÓÃÓÚÑéÖ¤Ò»¸öÈí¼þÊÇ·ñÌṩ×îÖÕÓû§»òÒµÎñËùÐèÒªµÄÊä³ö¡£

ͨ³££¬¹¦ÄܲâÊÔÉæ¼°ÆÀ¹ÀºÍ±È½Ïÿ¸öÈí¼þ¹¦ÄÜÓëÒµÎñÐèÇó¡£

ͨ¹ýÏòÈí¼þÌṩһЩÏà¹ØÊäÈë½øÐвâÊÔ£¬ÒÔ±ãÆÀ¹ÀÈí¼þµÄÊä³ö²¢²é¿´ÆäÓë»ù±¾ÒªÇóÏà±ÈÊÇ·ûºÏ¡¢¹ØÁª»¹ÊDZ仯µÄ¡£

´ËÍ⣬¹¦ÄܲâÊÔ»¹¿ÉÒÔ¼ì²éÈí¼þµÄ¿ÉÓÃÐÔ£¬ÀýÈçÈ·±£µ¼º½¹¦ÄÜÄܹ»°´ÒªÇó¹¤×÷¡£

ÔÚÎÒÃǵÄÀý×ÓÖУ¬ÎÒÃǽ«Î¢·þÎñ×÷Ϊһ¸öÈí¼þ£¬Õâ¸öÈí¼þ½«¸ù¾Ý×îÖÕÓû§µÄÒªÇóÀ´ÌṩһЩÊä³ö¡£

Ä¿µÄ

¹¦ÄܲâÊÔÓ¦º­¸ÇÎÒÃÇÓ¦ÓóÌÐòµÄÒÔÏ·½Ã棺

ÉÏÏÂÎÄÆô¶¯ - Õâ¿ÉÈ·±£·þÎñÔÚÉÏÏÂÎÄÖÐûÓгåÍ»£¬²¢ÇÒ¿ÉÒÔÔÚûÓÐÎÊÌâµÄÇé¿öϽøÐгõʼ»¯¡£

ÒµÎñÐèÇó/Óû§¹ÊÊ - °üÀ¨ËùÇëÇóµÄ¹¦ÄÜ¡£

»ù±¾ÉÏ£¬Ã¿¸ö£¨»ò´ó¶àÊý£©Óû§µÄ¹Êʶ¼Ó¦¸ÃÓÐ×Ô¼ºµÄרÓù¦ÄܲâÊÔ¡£

ÎÒÃDz»ÐèÒª±àдÉÏÏÂÎÄÆô¶¯²âÊÔ£¬ÒòΪֻҪÓÐÒ»¸ö¹¦ÄÜÒª²âÊÔ£¬ÄÇôÉÏÏÂÎÄÆô¶¯ÎÞÂÛÈçºÎ¶¼»á±»²âÊÔµ½µÄ¡£

ʵ¼ù

ΪÁËÑÝʾÈçºÎʹÓÃÎÒÃǵÄ×î¼Ñʵ¼ù£¬ÎÒÃÇÐèÒª±àдһЩʾ·¶·þÎñ´úÂë¡£

¾ÍÈÃÎÒÃÇ´ÓÍ·¿ªÊ¼°É¡£

ÈÎÎñ

ÎÒÃǵÄзþÎñÓÐÒÔÏÂÒªÇó£º

ÓÃÓÚ´æ´¢ºÍ¼ìË÷Óû§ÏêϸÐÅÏ¢µÄREST API¡£

ͨ¹ýREST¼ìË÷ÁªÏµ·þÎñÖеÄÁªÏµÈËÏêϸÐÅÏ¢£¬´Ó¶ø¼ìË÷Óû§ÏêϸÐÅÏ¢µÄREST API¡£

¼Ü¹¹Éè¼Æ

¶ÔÓÚ´ËÈÎÎñ£¬ÎÒÃǽ«»ùÓÚSpringƽ̨×÷Ϊ¿ò¼Ü£¬Ê¹ÓÃSpring Boot×÷ΪӦÓÃµÄÆô¶¯Õß¡£

ΪÁË´æ´¢Óû§ÏêϸÐÅÏ¢£¬ÎÒÃǽ«Ê¹ÓÃMariaDBÊý¾Ý¿â¡£

ÓÉÓÚ·þÎñÓ¦´æ´¢ºÍ¼ìË÷Óû§ÏêϸÐÅÏ¢£¬Òò´Ë½«ÆäÃüÃûΪÓû§ÏêϸÐÅÏ¢·þÎñÊǺϺõÂß¼­µÄ¡£

ÔÚʵÏÖ֮ǰ£¬Ó¦¸ÃʹÓÃ×é¼þͼÀ´¸üºÃµØÀí½âϵͳµÄÖ÷Òª×é¼þ£º

ʵÏÖ

ÒÔÏÂʾÀý´úÂë°üº¬Ðí¶àLombok×¢ÊÍ¡£

Äú¿ÉÒÔÔÚÍøÕ¾ÉϵÄdocsÎļþÖÐÕÒµ½Ã¿¸ö×¢Ê͵Ä˵Ã÷¡£

Ä£ÐÍ

Óû§ÏêÇéÄ£ÐÍ£º

@Value(staticConstructor = "of")
public class UserDetails {
String firstName; String lastName; public static UserDetails fromEntity(UserDetailsEntity entity) { return UserDetails.of(entity.getFirstName(), entity.getLastName()); } public UserDetailsEntity toEntity(long userId) { return new UserDetailsEntity(userId, firstName, lastName); }}

Óû§ÁªÏµÈËÄ£ÐÍ£º

@Value
public class UserContacts {
String email; String phone;}

¾ßÓлã×ÜÐÅÏ¢µÄÓû§Àࣺ

@Value(staticConstructor = "of")
public class User {
UserDetails userDetails; UserContacts userContacts;}

REST API

@RestController
@RequestMapping("user")
@AllArgsConstructor
public class UserController {
private final UserService userService; @GetMapping("/{userId}") //1 public User getUser(@PathVariable("userId") long userId) { return userService.getUser(userId); } @PostMapping("/{userId}/details") //2 public void saveUserDetails(@PathVariable("userId") long userId, @RequestBody UserDetails userDetails) { userService.saveDetails(userId, userDetails); } @GetMapping("/{userId}/details") //3 public UserDetails getUserDetails(@PathVariable("userId") long userId) { return userService.getDetails(userId); }

°´ID»ñÈ¡Óû§»ã×ÜÊý¾Ý

°´ID±£´æÓû§µÄÓû§ÏêϸÐÅÏ¢

°´ID»ñÈ¡Óû§ÏêϸÐÅÏ¢

¿Í»§ÁªÏµÈË·þÎñ

@Component
public class ContactsServiceClient {
private final RestTemplate restTemplate; private final String contactsServiceUrl; public ContactsServiceClient(final RestTemplateBuilder restTemplateBuilder, @Value("${contacts.service.url}") final String contactsServiceUrl) { this.restTemplate = restTemplateBuilder.build(); this.contactsServiceUrl = contactsServiceUrl; } public UserContacts getUserContacts(long userId) { URI uri = UriComponentsBuilder.fromHttpUrl(contactsServiceUrl + "/contacts") .queryParam("userId", userId).build().toUri(); return restTemplate.getForObject(uri, UserContacts.class); }}

ÏêϸÐÅϢʵÌå¼°Æä´æ´¢¿â

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDetailsEntity {
@Id private Long id; @Column private String firstName; @Column private String lastName;}
@Repository
public interface UserDetailsRepository extends JpaRepository {
}

Óû§·þÎñ

@Service
@AllArgsConstructor
public class UserService {
private final UserDetailsRepository userDetailsRepository; private final ContactsServiceClient contactsServiceClient; public User getUser(long userId) { UserDetailsEntity userDetailsEntity = userDetailsRepository.getOne(userId); //1 UserDetails userDetails = UserDetails.fromEntity(userDetailsEntity); UserContacts userContacts = contactsServiceClient.getUserContacts(userId); //2 return User.of(userDetails, userContacts); //3 } public void saveDetails(long userId, UserDetails userDetails) { UserDetailsEntity entity = userDetails.toEntity(userId); userDetailsRepository.save(entity); } public UserDetails getDetails(long userId) { UserDetailsEntity userDetailsEntity = userDetailsRepository.getOne(userId); return UserDetails.fromEntity(userDetailsEntity); }}

´ÓDB¼ìË÷Óû§ÏêϸÐÅÏ¢

´ÓÁªÏµÈË·þÎñÖмìË÷Óû§ÁªÏµÈË

·µ»Ø¾ßÓлã×ÜÊý¾ÝµÄÓû§

Ó¦ÓÃÆô¶¯ÀàºÍËüµÄÅäÖÃÎļþ

UserDetailsServiceApplication.java

@SpringBootApplication
public class UserDetailsServiceApplication {
public static void main(String[] args) { SpringApplication.run(UserDetailsServiceApplication
.class, args); }}

application.properties:

#contact service
contacts.service.url=http://www.prod.contact.service.com
#database
user.details.db.host=prod.maria.url.com
user.details.db.port=3306
user.details.db.schema=user_details
spring.datasource.url=jdbc:mariadb://${user.details.
db.host}:${user.details.db.port}/${user.details
.db.schema
}
spring.datasource.username=prod-username
spring.datasource.password=prod-password
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver

MavanÅäÖÃÎļþpom.xml

xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0 user-details-service 0.0.1-SNAPSHOT jar User details service com.tdanylchuk functional-tests-best-practices 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-web org.projectlombok lombok provided org.mariadb.jdbc mariadb-java-client 2.3.0 org.springframework.boot spring-boot-maven-plugin

×¢Ò⣺¸¸¼¶ÊÇ×Ô¶¨ÒåµÄfunctional-tests-best-practicesÏîÄ¿£¬Ëü¼Ì³ÐÁËspring-boot-starter-parent¡£ÉԺ󽫶Դ˽øÐÐ˵Ã÷¡£

Ŀ¼½á¹¹

Õ⼸ºõÊÇÎÒÃÇΪÂú×ã³õʼҪÇóËùÐèÒªµÄÒ»ÇУº±£´æºÍ¼ìË÷Óû§ÏêϸÐÅÏ¢£¬Í¨¹ýÁªÏµÈ˼ìË÷µÄÓû§ÏêϸÐÅÏ¢¡£

¹¦ÄܲâÊÔ

ÊÇʱºòÌí¼Ó¹¦ÄܲâÊÔÁË£¡¶ÔÓÚTDD£¨²âÊÔÇý¶¯¿ª·¢£©ÊÇʲô£¬ÄúÐèÒªÔÚ¾ßÌåʵÏÖ֮ǰÔĶÁ±¾½Ú¡£

λÖÃ

ÔÚ¿ªÊ¼Ö®Ç°£¬ÎÒÃÇÐèҪѡÔñ¹¦ÄܲâÊÔµÄλÖã»

ÓÐÁ½¸öÏà¶ÔºÏÊʵĵط½£º

ͨ¹ýÒ»¸ö¶ÀÁ¢µÄÎļþ¼Ð·ÅÔÚµ¥Ôª²âÊÔÏ£º

ÕâÊÇ¿ªÊ¼Ìí¼Ó¹¦ÄܲâÊÔ×î¼òµ¥£¬×î¿ìËٵķ½·¨£¬ËäÈ»ËüÓÐÒ»¸öºÜ´óµÄȱµã£ºÈç¹ûÄãÏëµ¥¶ÀÔËÐе¥Ôª²âÊÔ£¬ÄãÐèÒªÅųý¹¦ÄܲâÊÔÎļþ¼Ð¡£

ÄÇΪʲô²»ÄÜÿ´ÎÐ޸ĴúÂëʱ¶¼ÔËÐÐËùÓвâÊÔÄØ£¿

ÒòΪ¹¦ÄܲâÊÔÔÚ´ó¶àÊýÇé¿öÏÂÓëµ¥Ôª²âÊÔÏà±È¾ßÓо޴óµÄÖ´ÐÐʱ¼ä£¬Òò´ËÓ¦µ¥¶ÀÔËÐÐÒÔ½ÚÊ¡¿ª·¢Ê±¼ä¡£

×öΪһ¸ö¶ÀÁ¢ÏîÄ¿·ÅÖÃÔÚ¸¸ÏîĿϣº

¸¸POM£¨¾ÛºÏÏîÄ¿£©

Service씀
¹¦ÄܲâÊÔÏîÄ¿

ÕâÖÖ·½·¨ÓÅÓÚǰһÖÖ·½·¨ - ÎÒÃÇÔÚ·þÎñµ¥Ôª²âÊÔÖÐÓÐÒ»¸ö¶ÀÁ¢µÄ¹¦ÄܲâÊÔÄ£¿é£¬Òò´ËÎÒÃÇ¿ÉÒÔͨ¹ýµ¥¶ÀÔËÐе¥Ôª²âÊÔ»ò¹¦ÄܲâÊÔÀ´ÇáËÉÑéÖ¤Âß¼­¡£ÁíÒ»·½Ã棬ÕâÖÖ·½·¨ÐèÒªÒ»¸ö¶àÄ£¿éÏîÄ¿½á¹¹£¬Ó뵥ģ¿éÏîÄ¿Ïà±È£¬ÕâÖֽṹ¸ü¼ÓÀ§ÄÑ¡£

Äú¿ÉÄÜÒѾ­´ÓserviceµÄpom.xmlÖвµ½£¬¶ÔÓÚÎÒÃǵÄÇé¿ö£¬ÎÒÃǽ«Ñ¡ÔñµÚ¶þÖÖ·½·¨¡£

¸¸pom.xmlÎļþ

xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0 com.tdanylchuk functional-tests-best-practices 0.0.1-SNAPSHOT pom Functional tests best practices parent project org.springframework.boot spring-boot-starter-parent 2.0.4.RELEASE user-details-service user-details-service-functional-tests UTF-8 UTF-8 1.8

spring-boot-starter-parentÊǸ¸POMµÄ¸¸ÏîÄ¿¡£Í¨¹ýÕâÖÖ·½Ê½£¬ÎÒÃÇΪSpringÌṩÁËÒÀÀµ¹ÜÀí¡£

Ä£¿éÉùÃ÷¡£×¢Ò⣺˳ÐòºÜÖØÒª£¬¹¦ÄܲâÊÔӦʼÖÕ·ÅÖÃÔÚ×îºó¡£

°¸Àý

¶ÔÓÚÌôÑ¡°¸ÀýÒÔº­¸Ç¹¦ÄܲâÊÔ£¬ÎÒÃÇÐèÒª¿¼ÂÇÁ½¼þ´óÊ£º

¹¦ÄÜÐèÇó - »ù±¾ÉÏ£¬Ã¿¸öÐèÇó¶¼Ó¦ÓÐ×Ô¼ºµÄ¹¦ÄܲâÊÔ¡£

Ö´ÐÐʱ¼ä³¤ - °¸ÀýÓ¦¸Ã²àÖØÓÚÓ¦ÓóÌÐòµÄ¹Ø¼ü²¿·Ö£¬ÕâÓëµ¥Ôª²âÊÔÏà·´µÄÊÇËü»¹Òªº­¸Çÿ¸ö´ÎÒª°¸Àý¡£·ñÔò£¬¹¹½¨Ê±¼ä½«ÊǾ޴óµÄ¡£

¹¹½¨

Êǵ쬲âÊÔ»¹ÐèÒª¿¼Âǹ¹½¨Îļþ£¬ÓÈÆäÊÇÄÇЩÊÜÖ´ÐÐʱ¼äÓ°Ïì³Ì¶È±È½Ï´óµÄ¹¦ÄܲâÊÔ£¬±ÈÈçһЩҵÎñÂß¼­¿ÉÄÜ»áËæ×Åʱ¼äµÄÍÆÒÆ±äµÃ¹ýÓÚ¸´ÔÓ¡£

´ËÍ⣬¹¦ÄܲâÊÔÓ¦¸ÃÊÇ¿Éά»¤µÄ¡£ÕâÒâζ×Å£¬ÔÚ¹¦ÄÜת»»µÄÇé¿öÏ£¬¹¦ÄܲâÊÔ¶Ô¿ª·¢ÈËÔ±À´Ëµ²»ÊÇÒ»¼þÁîÈËÍ·ÌÛµÄÎÊÌâ¡£

²½Öè

²½Ö裨Ҳ³ÆÎª¹Ì¶¨½Úµã£©ÊÇÒ»ÖÖ·âװÿ¸öͨÐÅͨµÀÂß¼­µÄ·½·¨¡£

ÿ¸öͨµÀ¶¼Ó¦ÓÐ×Ô¼ºµÄ²½Öè¶ÔÏó£¬ÓëÆäËû²½Öè¸ôÀë¡£

ÔÚÎÒÃǵÄÀý×ÓÖУ¬ÎÒÃÇÓÐÁ½¸öͨÐÅÇþµÀ£º

Óû§ÏêϸÐÅÏ¢·þÎñµÄREST API£¨ÊäÈëÆµµÀ£©

ÁªÏµÈË·þÎñµÄREST API£¨Êä³öƵµÀ£©

¶ÔÓÚÊäÈëÆµµÀµÄREST£¬ÎÒÃǽ«Ê¹ÓÃÃûΪREST AssuredµÄ¿â¡£

ÓëÎÒÃÇ֮ǰʹÓÃMockMvc½øÐÐÑéÖ¤REST APIµÄ¼¯³É²âÊÔÏà±È£¬ÕâÀïÎÒÃÇʹÓøü¶àµÄºÚºÐ²âÊÔÀ´±ÜÃâ²âÊÔÄ£Äâ¶ÔÏóÆÆ»µSpringµÄÉÏÏÂÎÄ¡£

ÖÁÓÚRESTÊä³öƵµÀ£¬ÎÒÃǽ«Ê¹ÓÃWireMock¡£

ÎÒÃDz»»áÈÃSpringÓÃRESTÄ£°åÌæ»»Ä£ÄâµÄÄ£°å¡£

Ïà·´£¬WireMockÒýÇæÊ¹ÓõÄjetty·þÎñÆ÷½«ÓëÎÒÃǵķþÎñÒ»ÆðÆô¶¯£¬ÒÔÄ£ÄâÕæÕýµÄÍⲿREST·þÎñ¡£

Óû§ÏêÇé²½Öè

@Component
public class UserDetailsServiceSteps implements
ApplicationListener {
private int servicePort; public String getUser(long userId)
{
return given().port(servicePort) .when().get("user/" + userId) .then().statusCode(200).contentType
(ContentType.JSON).extract().asString(); }
public void saveUserDetails(long userId, String body) { given().port(servicePort).body(body).contentType
(ContentType.JSON) .when().post("user/" + userId + "/details") .then().statusCode(200);
}
public String getUserDetails(long userId) {
return given().port(servicePort) .when().get
("user/" + userId + "/details") .then().statusCode(200).contentType(ContentType.JSON).
extract().asString(); }
@Override public void onApplicationEvent(@NotNull
WebServerInitializedEvent webServerInitializedEvent)
{ this.servicePort = webServerInitializedEvent.
getWebServer().getPort();
}}

¾ÍÏñÄã´Ósteps¶ÔÏóÖп´µ½µÄ£¬Ã¿¸öAPI¶¼ÓÐ×Ô¼ºµÄ·½·¨¡£

ĬÈÏÇé¿öÏ£¬REST Assured½«·ÃÎÊlocalhostµÄAPI£¬µ«ÐèÒªÖ¸¶¨¶Ë¿ÚºÅ·ñÔòÎÒÃǵķþÎñ½«Ê¹ÓÃËæ»ú¶Ë¿ÚÀ´Æô¶¯¡£

ΪÁËÇø·Ö¶Ë¿ÚºÅ£¬ÎÒÃÇÓ¦¸Ã´ÓWebServerInitializedEventÖÐÈ¥»ñÈ¡¶Ë¿Ú¡£

×¢Ò⣺@LocalServerPort×¢½â²»ÄÜÔÚ´Ë´¦Ê¹Óã¬ÒòΪÔÚSpring Boot-embeddedÈÝÆ÷Æô¶¯Ö®Ç°¾Í³õʼ»¯Á˲½ÖèµÄbean¡£

ÁªÏµÈË·þÎñ²½Öè

@Component
public class ContactsServiceSteps {
public void expectGetUserContacts(long userId,
String body)
{
stubFor(get(urlPathMatching("/contacts")).withQueryParam
("userId", equalTo(String.valueOf(userId))) .willReturn(okJson(body)));
}}

ÔÚÕâÀÎÒÃÇÐèÒªÒÔÓë´ÓÎÒÃǵÄÓ¦ÓóÌÐòµ÷ÓÃÔ¶³Ì·þÎñʱÏàͬµÄ·½Ê½À´Ä£Äâ·þÎñÆ÷µÄ¶Ë¿Ú¡¢²ÎÊýµÈ¡£

Êý¾Ý¿â

ÎÒÃǵķþÎñÊǽ«Êý¾Ý´æ´¢ÔÚMaria DBÖУ¬µ«¾Í¹¦ÄܲâÊÔ¶øÑÔ£¬Êý¾ÝµÄ´æ´¢Î»Öò¢²»ÖØÒª£¬Òò´Ë°´ÕպںвâÊÔµÄÒªÇó£¬ÎÒÃDz»ÐèÒªÔÚ²âÊÔÖÐÌá¼°Êý¾Ý¿â¡£

¼ÙÉèÔÚδÀ´£¬ÎÒÃÇ¿¼Âǽ«Maria DB¸ü¸ÄΪijЩNoSQL½â¾ö·½°¸£¬ÄÇô¹¦ÄܲâÊÔÓ¦²»ÐèÒª×ö³ö¸Ä¶¯¡£

ÄÇô£¬Îª´Ë½â¾ö·½°¸ÊÇʲô£¿

µ±È»£¬ÎÒÃÇ¿ÉÒÔʹÓÃǶÈëʽ½â¾ö·½°¸£¬¾ÍÏñÔÚ¼¯³É²âÊÔÖÐʹÓÃH2Êý¾Ý¿âÒ»Ñù£¬µ«ÔÚÉú²úʱ£¬ÎÒÃǵķþÎñÓÖ½«Ê¹ÓÃMaria DB£¬Õâ¿ÉÄܻᵼÖÂijЩµØ·½³ö´í¡£

ÀýÈ磬ÎÒÃÇÓÐÒ»¸öÃûΪMAXVALUEµÄÁУ¬²¢Õë¶ÔH2ÔËÐвâÊÔ£¬Ò»ÇÐÕý³£¡£µ«ÊÇ£¬ÔÚÉú²úÖУ¬·þÎñʧ°ÜÁË£¬ÒòΪÕâÊÇMariaDBÖеÄÒ»¸öÔ¤Áô¹Ø¼ü×Ö£¬ÕâÒâζ×ÅÎÒÃǵIJâÊÔ²»ÈçÔ¤ÆÚµÄÄÇôºÃ£¬²¢ÇÒÔÚ½«·þÎñ·¢²¼Ö®Ç°¿ÉÄÜÀË·Ñ´óÁ¿Ê±¼äÀ´½â¾öÕâ¸öÎÊÌâ¡£

±ÜÃâÕâÖÖÇé¿öµÄΨһ·½·¨ÊÇÔÚ²âÊÔÖÐʹÓÃÕæÕýµÄMaria DB¡£Í¬Ê±£¬ÎÒÃÇÐèҪȷ±£ÎÒÃǵIJâÊÔ¿ÉÒÔÔÚ±¾µØÖ´ÐУ¬¶øÎÞÐèÉèÖÃMaria DBµÄÈÎºÎÆäËûÁÙʱ»·¾³¡£

ΪÁ˽â¾öÕâ¸öÎÊÌ⣬ÎÒÃÇʹÓÃtestcontainersÏîÄ¿£¬¸ÃÏîÄ¿Ìṩ³£¼ûµÄÇáÁ¿¼¶Êý¾Ý¿âʵÀý£º¿ÉÒÔÔÚSelenium Webä¯ÀÀÆ÷»òDockerÈÝÆ÷ÖÐÔËÐеġ£

µ«testcontainers¿â²»Ö§³ÖSpring BootµÄ¿ªÏä¼´Óá£Òò´Ë£¬ÎÒÃǽ«Ê¹ÓÃÁíÒ»¸öÃûΪtestcontainers-spring-bootµÄ¿â£¬¶ø²»ÊÇΪMariaDB±àд×Ô¶¨ÒåµÄGeneric Container²¢½«Æä×¢ÈëSpring Boot¡£testcontainers-spring-bootÖ§³Ö×î³£Óõļ¼Êõ£¬²¢¿ÉÒÔÖ±½ÓÔÚÄúµÄ·þÎñÖÐʹÓãºMariaDB£¬Couchbase£¬Kafka£¬Aerospike£¬MemSQL£¬Redis£¬neo4j£¬Zookeeper£¬PostgreSQL£¬ElasticSearchµÈµÈ¡£

Òª½«ÕæÕýµÄMaria DB×¢ÈëÎÒÃǵIJâÊÔ£¬ÎÒÃÇÖ»ÐèÒª½«ÏàÓ¦µÄÒÀÀµÏîÌí¼Óµ½ÎÒÃǵÄuser-details-service-functional-testsÏîÄ¿pom.xmlÎļþÖУ¬ÈçÏÂËùʾ£º

<dependency>
<groupId>com.playtika.testcontainers</groupId>
<artifactId>embedded-mariadb</artifactId>
<version>1.9</version>
<scope>test</scope>
</dependency>

Èç¹ûÄúµÄ·þÎñ²»Ê¹ÓÃSpring Cloud£¬ÔòÓ¦°ÑÏÂÊöÒÀÀµ¸úÉÏÊöÒÀÀµÒ»²¢Ìí¼Ó£º

<dependency>
<groupId> org.springframework.cloud </groupId>
<artifactId> spring-cloud-context </artifactId>
<version> 2.0.1.RELEASE </version>
<scope> test </scope>
</dependency>

ËüÐèÒªÔÚSpring BootÉÏÏÂÎÄÆô¶¯Ö®Ç°Îªdockerized×ÊÔ´½øÐÐÒýµ¼¡£

ÕâÖÖ·½·¨ÏÔÈ»ÓкܶàÓŵ㡣

ÓÉÓÚÎÒÃÇÓµÓС°ÕæÊµ¡±×ÊÔ´£¬Òò´ËÈç¹ûÎÞ·¨¶ÔËùÐè×ÊÔ´½øÐÐÕæÕýµÄÁ¬½Ó²âÊÔ£¬ÔòÎÞÐèÔÚ´úÂëÖбàд½â¾ö°ì·¨¡£

²»ÐÒµÄÊÇ£¬Õâ¸ö½â¾ö·½°¸´øÀ´ÁËÒ»¸ö¾Þ´óµÄȱµã - ²âÊÔÖ»ÄÜÔÚ°²×°DockerµÄ»·¾³ÖÐÔËÐС£

ÕâÒâζ×ÅÄúµÄ¹¤×÷Õ¾ºÍCI¹¤¾ßÓ¦¸Ã°²×°Docker¡£

´ËÍ⣬ÄúÓ¦¸Ã×¼±¸ºÃ¸ü¶àµÄʱ¼äÀ´Ö´ÐÐÄúµÄ²âÊÔ¡£

¸¸²âÊÔÀà

ÒòΪִÐÐʱ¼äºÜÖØÒª£¬ËùÒÔÎÒÃÇÐèÒª±ÜÃâ¶Ôÿ¸ö²âÊÔ½øÐжà¸öÉÏÏÂÎļÓÔØ£¬Òò´ËDockerÈÝÆ÷½«¶ÔËùÓвâÊÔ½öÆô¶¯Ò»´Î¡£

SpringĬÈÏÆôÓÃÁËÉÏÏÂÎÄ»º´æ¹¦ÄÜ£¬µ«ÎÒÃÇÐèҪСÐÄʹÓãºÍ¨¹ýÌí¼Ó¼òµ¥µÄ@MockBean×¢½â£¬ÎÒÃǽ«Ç¿ÖÆSpringΪģÄâµÄbean´´½¨Ò»¸öеÄÉÏÏÂÎĶø²»ÊÇÖØÓÃÏÖÓеÄÉÏÏÂÎÄ¡£

´ËÎÊÌâµÄ½â¾ö·½°¸ÊÇ´´½¨µ¥¸ö¸¸³éÏóÀ࣬¸ÃÀཫ°üº¬ËùÓÐÐèÒªµÄSpring×¢½â£¬ÒÔÈ·±£ËùÓвâÊÔÌ×¼þÖØÓõ¥¸öÉÏÏÂÎÄ£º

@RunWith ( SpringRunner . class )
@SpringBootTest
(
classes = UserDetailsServiceApplication . class , //1
webEnvironment = SpringBootTest . WebEnvironment.RANDOM_PORT)
//2
@ActiveProfiles ( "test" ) //3
public abstract class BaseFunctionalTest
{
@Rule
public WireMockRule contactsServiceMock = new
WireMockRule (options().port( 8777 )); //4
@Autowired //5
protected UserDetailsServiceStepsuserDetailsServiceSteps;
@Autowired
protected ContactsServiceSteps contactsServiceSteps;
@TestConfiguration //6
@ComponentScan
(
"com.tdanylchuk.user.details.steps"
)
public static class StepsConfiguration
{
}
}

Ö¸¶¨Spring BootµÄ²âÊÔ×¢½âÒÔ¼ÓÔØÎÒÃÇ·þÎñµÄÖ÷ÒªÅäÖÃÀà¡£

Òýµ¼Ê¹ÓÃWebÉú²ú»·¾³£¨Ä¬ÈÏÇé¿öϽ«Ê¹ÓÃÄ£ÄâµÄ»·¾³£©¡£

²âÊÔ»·¾³µÄÅäÖÃÏî±»¼ÓÔØÔÚÁËapplication-test.propertiesÎļþÖУ¬ÆäÖаüº¬ÁËһЩÉú²ú»·¾³µÄÊôÐÔ£¬ÀýÈçURL£¬Óû§£¬ÃÜÂëµÈ¡£

WireMockRuletͨ¹ýÆô¶¯jetty·þÎñÆ÷ÒÔ±ãÔÚÌṩµÄ¶Ë¿ÚÉϽøÐÐÁ¬½Ó¡£

²½ÖèÊÇ×Ô¶¯¼ÓÔØÎªprotectedÊôÐԵģ¬ÕâÑùÿһ¸ö²½Öè»áÔÚÿһ¸ö²âÊԵõ½·ÃÎÊ¡£

@TestConfiguration×¢½â»áͨ¹ýɨÃè°üÃûÀ´¼ÓÔØÉÏÏÂÎĵIJ½Öè¡£

ÔÚÕâÀÎÒÃÇÊÔͼ²»ÐÞ¸ÄÉÏÏÂÎÄ£¬ÕâÑùµÄºÃ´¦Ê±ÔÚºóÆÚÓÃÓÚÉú²ú»·¾³Ê±£¬Ö»ÐèÒªÍùÉÏÏÂÎÄÌí¼ÓһЩutilÏî¾Í¿ÉÒÔÁË£¬ÀýÈç²½ÖèºÍÊôÐÔ¸²¸Ç¡£

ʹÓÃ@MockBean×¢½âÊDz»ºÃµÄ×ö·¨£¬ÒòΪËü»áÓÃmockÌæ»»²¿·ÖÓ¦ÓóÌÐò£¬ËùÒÔÕⲿ·Ö³ÌÐò½«ÊÇûÓо­¹ý²âÊԵġ£

ÔÚ²»¿É±ÜÃâµÄÇé¿öÏ - ÀýÈçÔÚÒµÎñÖлñÈ¡µ±Ç°Ê±¼äSystem.currentTimeMillis()£¬ÕâÑùµÄ´úÂëÓ¦¸Ã±»Öع¹£¬×îºÃʹÓÃClock¶ÔÏó£ºclock.millis()¡£²¢ÇÒ£¬ÔÚ¹¦ÄܲâÊÔÖУ¬Ó¦¸ÃÄ£ÄâClock¶ÔÏóÒÔ±ãÑéÖ¤½á¹û¡£

²âÊÔÊôÐÔ

application-test.properties:

#contact service #1
contacts.service.url=http:
//localhost:8777
#database #2
user.details.db.host=${embedded.mariadb.host}
user.details.db.port=${embedded.mariadb.port}
user.details.db.schema=${embedded.mariadb.schema}
spring.datasource.username=${embedded.mariadb.user}
spring.datasource.password=${embedded.mariadb.password}

#3
spring.jpa.hibernate.ddl-
auto
=create-drop

ʹÓÃWireMock jetty·þÎñÆ÷½Úµã¶ø²»ÊÇÉú²ú»·¾³ÏµÄÁªÏµÈË·þÎñURL¡£

Êý¾Ý¿âÊôÐÔµÄÖØÔØ¡£×¢Ò⣺ÕâЩÊôÐÔÓÉspring-boo-test-containers¿âÌṩ¡£

ÔÚ²âÊÔÖУ¬Êý¾Ý¿âµÄ±í½«ÓÉHibernate´´½¨¡£

×ÔÎÒ²âÊÔ

ΪÁ˽øÐÐÕâÏî²âÊÔ£¬ÎÒÃÇ×öÁ˺ܶà×¼±¸¹¤×÷£¬ÈÃÎÒÃÇÏȳòÒ»ÑÛ£º

public class RestUserDetailsTest extends
BaseFunctionalTest
{
private static final long USER_ID = 32343L
;

private final String
userContactsResponse = readFile(
"json/user-contacts.json"
);

private final String
userDetails = readFile(
"json/user-details.json"
);

private final

String expectedUserResponse = readFile(
"json/user.json"
);

@Test
public void shouldSaveUserDetailsAndRetrieveUser()
throws Exception
{
//when
userDetailsServiceSteps.saveUserDetails(USER_ID, userDetails);

//and
contactsServiceSteps.expectGetUserContacts(USER_ID, userContactsResponse);
//then
String actualUserResponse=userDetailsServiceSteps.
getUser(USER_ID);

//expect
JSONAssert .assertEquals(expectedUserResponse, actualUserResponse,
false
);
}
}

ÔÚÒÔǰ£¬´ò×®ºÍ¶ÏÑÔ¶¼ÊÇͨ¹ýJSONÎļþÀ´´´½¨Ê¹Óõġ£ÕâÖÖ·½Ê½£¬°ÑÇëÇóºÍÏìÓ¦µÄ¸ñʽͬʱ½øÐÐÁËÑéÖ¤¡£µ«ÔÚ¹¦ÄܲâÊÔÀÎÒÃÇ×îºÃ²»ÒªÊ¹ÓÃÈ·¶¨¸ñʽµÄ²âÊÔÊý¾Ý£¬¶øÊÇʹÓÃÉú²ú»·¾³ÖÐÇëÇó/ÏìÓ¦Êý¾ÝµÄ¸±±¾¡£

ÓÉÓÚÕû¸öÂß¼­·â×°ÔÚ²½Öè¡¢ÅäÖúÍJSONÎļþÖУ¬Èç¹û¸Ä¶¯Ó빦ÄÜÎ޹أ¬ÄÇô²âÊÔ½«±£³Ö²»±ä¡£ÀýÈ磺

ÏìÓ¦Êý¾Ý¸ñʽµÄ¸ü¸Ä - Ö»Ó¦ÐÞ¸ÄJSON²âÊÔÎļþ¡£

ÁªÏµÈË·þÎñ½Úµã¸ü¸Ä - Ó¦ÐÞ¸ÄContactsServiceSteps¶ÔÏó¡£

Maria DBÌæ»»ÎªNo SQL DB - Ó¦ÐÞ¸Äpom.xmlºÍtest propertiesÎļþ¡£

¹¦ÄܲâÊÔÏîÄ¿µÄPOMÎļþ

<?xml version= "1.0" encoding= "UTF-8" ?>

<roject xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion> 4.0.0 </modelVersion>
<artifactId> user-details-service-functional-tests </artifactId>
<version> 0.0.1-SNAPSHOT </version>
<name>User details service functional tests </name>
<parent>
<groupId> com.tdanylchuk </groupId>
<artifactId> functional-tests-best-practices </artifactId>
<version> 0.0.1-SNAPSHOT </version>
</parent>
<dependencies>
<dependency>
<!--1--
<groupId>com.tdanylchuk </groupId>
<artifactId> user-details-service </artifactId>
<version> ${project.version} </version>
<scope> test </scope>
</dependency>
<dependency>
<groupId>org.springframework.boot </groupId>
<artifactId> spring-boot-starter-test </artifactId>
<scope> test </scope>
</dependency>
<dependency>
<groupId> org.springframework.cloud
</groupId>
<artifactId> spring-cloud-context </artifactId>
<version> 2.0.1.RELEASE </version>
<scope> test </scope>
</dependency>
<dependency>
<groupId> com.playtika.testcontainers </groupId>
<artifactId> embedded-mariadb </artifactId>
<version> 1.9 </version>
<scope> test </scope>
</dependency>
<dependency>
<groupId> com.github.tomakehurst </groupId>
<artifactId> wiremock </artifactId>
<version> 2.18.0 </version>
<scope> test </scope>
</dependency>
<dependency>
<groupId> io.rest-assured </groupId>
<artifactId> rest-assured </artifactId>
<scope> test </scope>
</dependency>
</dependencies>
</project>

Óû§ÏêϸÐÅÏ¢·þÎñ×÷ΪÒÀÀµÏîÌí¼Ó£¬Òò´ËËü¿ÉÒÔÓÉSpringBootTest½øÐмÓÔØ¡£

Ŀ¼½á¹¹

×ܶøÑÔÖ®£¬ÎÒÃÇÓÐÁËÏÂÒ»¸öÏîÄ¿µÄĿ¼½á¹¹¡£

Ïò·þÎñÌí¼Ó¹¦Äܲ»»á¸Ä±äµ±Ç°Ä¿Â¼½á¹¹£¬Ö»»áÀ©Õ¹Ëü¡£

ͨ¹ýÌí¼Ó¶îÍâµÄ²½Ö裬±ÈÈçÌí¼ÓÁ˸ü¶àµÄͨÐÅÇþµÀ£¬ÄÇôutilsÎļþ¼ÐÏ¿ÉÒÔÌí¼ÓºÜ¶à³£Ó÷½·¨;

±ÈÈç´øÓвâÊÔÊý¾ÝµÄÐÂÎļþ; µ±È»»¹ÓÐÕë¶Ôÿ¸ö¹¦ÄÜÒªÇóµÄ¸½¼Ó²âÊÔ¡£

×ܽá

ÔÚ±¾ÎÄÖУ¬ÎÒÃÇ»ùÓÚ¸ø¶¨µÄÒªÇó¹¹½¨ÁËÒ»¸öеÄ΢·þÎñ£¬²¢Í¨¹ý¹¦ÄܲâÊÔÀ´Âú×ãÕâЩҪÇó¡£

ÔÚ²âÊÔÖУ¬ÎÒÃÇʹÓúںÐÀàÐ͵IJâÊÔ£¬ÎÒÃdz¢ÊÔ²»¸Ä±äÓ¦ÓóÌÐòµÄÄÚ²¿²¿·Ö£¬¶øÊÇ×÷ΪÆÕͨ¿Í»§¶Ë´ÓÍⲿÓëËü½øÐÐͨÐÅ£¬ÒÔ¾¡¿ÉÄÜ¶àµØÄ£ÄâÉú²úÐÐΪ¡£

ͬʱ£¬ÎÒÃǵ춨Á˹¦ÄܲâÊԼܹ¹µÄ»ù´¡£¬Òò´ËδÀ´µÄ·þÎñ¸ü¸Ä²»ÐèÒªÖØ¹¹ÏÖÓвâÊÔ£¬²¢ÇÒ¾¡¿ÉÄܽ«Ìí¼ÓеIJâÊÔ¼òµ¥»¯¡£

 
   
2838 ´Îä¯ÀÀ       27
Ïà¹ØÎÄÕÂ

΢·þÎñ²âÊÔÖ®µ¥Ôª²âÊÔ
һƪͼÎÄ´øÄãÁ˽â°×ºÐ²âÊÔÓÃÀýÉè¼Æ·½·¨
È«ÃæµÄÖÊÁ¿±£ÕÏÌåϵ֮»Ø¹é²âÊÔ²ßÂÔ
È˹¤ÖÇÄÜ×Ô¶¯»¯²âÊÔ̽Ë÷
Ïà¹ØÎĵµ

×Ô¶¯»¯½Ó¿Ú²âÊÔʵ¼ù֮·
jenkins³ÖÐø¼¯³É²âÊÔ
ÐÔÄܲâÊÔÕï¶Ï·ÖÎöÓëÓÅ»¯
ÐÔÄܲâÊÔʵÀý
Ïà¹Ø¿Î³Ì

³ÖÐø¼¯³É²âÊÔ×î¼Ñʵ¼ù
×Ô¶¯»¯²âÊÔÌåϵ½¨ÉèÓë×î¼Ñʵ¼ù
²âÊԼܹ¹µÄ¹¹½¨ÓëÓ¦ÓÃʵ¼ù
DevOpsʱ´úµÄ²âÊÔ¼¼ÊõÓë×î¼Ñʵ¼ù