日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

byteman_使用Byteman和JUnit进行故障注入

發布時間:2023/12/3 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 byteman_使用Byteman和JUnit进行故障注入 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

byteman

我們的應用程序獨立存在的時間已經很久了。 如今,應用程序是一種非常復雜的野獸,它們使用無數的API和協議相互通信,將數據存儲在傳統或NoSQL數據庫中,通過網絡發送消息和事件……例如,您多久考慮一次例如數據庫的情況當您的應用程序正在主動查詢時發生故障? 還是某個API端點突然開始拒絕連接? 將此類事故作為測試套件的一部分覆蓋不是很好嗎? 這就是故障注入和Byteman框架所要解決的問題。 例如,我們將構建一個現實的,功能完善的Spring應用程序,該應用程序使用Hibernate / JPA訪問MySQL數據庫并管理客戶。 作為應用程序的JUnit集成測試套件的一部分,我們將包括三種測試用例:

  • 儲存/尋找顧客
  • 存儲客戶并嘗試在數據庫宕機時查詢數據庫(故障模擬)
  • 存儲客戶和數據庫查詢超時(故障模擬)

在本地開發箱上運行應用程序只有兩個先決條件:

  • MySQL服務器已安裝并具有客戶數據庫
  • 已安裝Oracle JDK ,并且JAVA_HOME環境變量指向它

話雖這么說,我們已經準備好出發了。 首先,讓我們描述我們的域模型,該域模型由具有ID和單個屬性名的單個Customer類組成。 看起來很簡單:

package com.example.spring.domain;import java.io.Serializable;import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table;@Entity @Table( name = "customers" ) public class Customer implements Serializable{private static final long serialVersionUID = 1L;@Id@GeneratedValue@Column(name = "id", unique = true, nullable = false)private long id;@Column(name = "name", nullable = false)private String name;public Customer() {}public Customer( final String name ) {this.name = name;}public long getId() {return this.id;}protected void setId( final long id ) {this.id = id;}public String getName() {return this.name;}public void setName( final String name ) {this.name = name;} }

為簡單起見,服務層與數據訪問層混合在一起并直接調用數據庫。 這是我們的CustomerService實現:

package com.example.spring.services;import javax.persistence.EntityManager; import javax.persistence.PersistenceContext;import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import com.example.spring.domain.Customer;@Service public class CustomerService {@PersistenceContext private EntityManager entityManager;@Transactional( readOnly = true )public Customer find( long id ) {return this.entityManager.find( Customer.class, id );}@Transactional( readOnly = false )public Customer create( final String name ) {final Customer customer = new Customer( name );this.entityManager.persist(customer);return customer;}@Transactional( readOnly = false )public void deleteAll() {this.entityManager.createQuery( "delete from Customer" ).executeUpdate();} }

最后, Spring應用程序上下文定義了數據源和事務管理器。 這里需要注意的一點是:由于我們不會引入數據訪問層( @Repository )類,為了使Spring正確執行異常轉換,我們將PersistenceExceptionTranslationPostProcessor實例定義為后處理服務類( @Service )。 其他一切都應該非常熟悉。

package com.example.spring.config;import java.util.Properties;import javax.sql.DataSource;import org.hibernate.dialect.MySQL5InnoDBDialect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement;import com.example.spring.services.CustomerService;@EnableTransactionManagement @Configuration @ComponentScan( basePackageClasses = CustomerService.class ) public class AppConfig {@Beanpublic PersistenceExceptionTranslationPostProcessor exceptionTranslationPostProcessor() {final PersistenceExceptionTranslationPostProcessor processor = new PersistenceExceptionTranslationPostProcessor();processor.setRepositoryAnnotationType( Service.class );return processor;}@Beanpublic HibernateJpaVendorAdapter hibernateJpaVendorAdapter() {final HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();adapter.setDatabase( Database.MYSQL );adapter.setShowSql( false );return adapter;}@Beanpublic LocalContainerEntityManagerFactoryBean entityManager() throws Throwable {final LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();entityManager.setPersistenceUnitName( "customers" );entityManager.setDataSource( dataSource() );entityManager.setJpaVendorAdapter( hibernateJpaVendorAdapter() );final Properties properties = new Properties();properties.setProperty("hibernate.dialect", MySQL5InnoDBDialect.class.getName());properties.setProperty("hibernate.hbm2ddl.auto", "create-drop" );entityManager.setJpaProperties( properties );return entityManager;}@Beanpublic DataSource dataSource() {final DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName( com.mysql.jdbc.Driver.class.getName() );dataSource.setUrl( "jdbc:mysql://localhost/customers?enableQueryTimeouts=true" );dataSource.setUsername( "root" );dataSource.setPassword( "" );return dataSource;}@Beanpublic PlatformTransactionManager transactionManager() throws Throwable {return new JpaTransactionManager( this.entityManager().getObject() );} }

現在,讓我們添加一個簡單的JUnit測試用例,以驗證我們的Spring應用程序確實按預期工作。 在此之前,應創建數據庫客戶 :

> mysql -u root mysql> create database customers; Query OK, 1 row affected (0.00 sec)

這是一個CustomerServiceTestCase ,目前,它具有單個測試以創建客戶并驗證其是否已創建。

package com.example.spring;import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat;import javax.inject.Inject;import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader;import com.example.spring.config.AppConfig; import com.example.spring.domain.Customer; import com.example.spring.services.CustomerService;@RunWith( SpringJUnit4ClassRunner.class ) @ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = { AppConfig.class } ) public class CustomerServiceTestCase {@Inject private CustomerService customerService; @Afterpublic void tearDown() {customerService.deleteAll();}@Testpublic void testCreateCustomerAndVerifyItHasBeenCreated() throws Exception {Customer customer = customerService.create( "Customer A" );assertThat( customerService.find( customer.getId() ), notNullValue() );} }

看起來很簡單明了。 現在,讓我們考慮成功創建客戶但由于查詢超時而導致查找失敗的情況。 為此,我們需要Byteman的幫助。 簡而言之, Byteman是字節碼操作框架。 這是一個Java代理實現,可與JVM一起運行(或附加到JVM)并修改正在運行的應用程序字節碼,從而改變其行為。 Byteman有一個很好的文檔,并且擁有豐富的規則定義集,可以執行開發人員可以想到的幾乎所有事情。 而且,它與JUnit框架具有很好的集成。 在該主題上,應該使用@RunWith(BMUnitRunner.class)運行Byteman測試,但是我們已經在使用@RunWith(SpringJUnit4ClassRunner.class),并且JUnit不允許指定多個測試運行程序。 除非您熟悉JUnit @Rule機制,否則這似乎是一個問題。 事實證明,將BMUnitRunner轉換為JUnit規則非常容易:

package com.example.spring;import org.jboss.byteman.contrib.bmunit.BMUnitRunner; import org.junit.rules.MethodRule; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement;public class BytemanRule extends BMUnitRunner implements MethodRule {public static BytemanRule create( Class< ? > klass ) {try {return new BytemanRule( klass ); } catch( InitializationError ex ) { throw new RuntimeException( ex ); }}private BytemanRule( Class klass ) throws InitializationError {super( klass );}@Overridepublic Statement apply( final Statement statement, final FrameworkMethod method, final Object target ) {Statement result = addMethodMultiRuleLoader( statement, method ); if( result == statement ) {result = addMethodSingleRuleLoader( statement, method );}return result;} }

JUnit @Rule注入就這么簡單:

@Rule public BytemanRule byteman = BytemanRule.create( CustomerServiceTestCase.class );

容易吧? 我們前面提到的場景可以改寫一下:當執行從“客戶”表中選擇的JDBC語句執行時,我們應該因超時異常而失敗。 這是帶有附加Byteman批注的JUnit測試用例的外觀:

@Test( expected = DataAccessException.class )@BMRule(name = "introduce timeout while accessing MySQL database",targetClass = "com.mysql.jdbc.PreparedStatement",targetMethod = "executeQuery",targetLocation = "AT ENTRY",condition = "$0.originalSql.startsWith( \"select\" ) && !flagged( \"timeout\" )",action = "flag( \"timeout\" ); throw new com.mysql.jdbc.exceptions.MySQLTimeoutException( \"Statement timed out (simulated)\" )")public void testCreateCustomerWhileDatabaseIsTimingOut() {Customer customer = customerService.create( "Customer A" );customerService.find( customer.getId() );}

我們可以這樣寫:“當有人調用PreparedStatement類的executeQuery方法,并且查詢以'SELECT'開始時,將拋出MySQLTimeoutException ,并且它應該只發生一次(由超時標志控制)”。 運行此測試用例將在控制臺中打印stacktrace,并期望引發DataAccessException :

com.mysql.jdbc.exceptions.MySQLTimeoutException: Statement timed out (simulated)at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.7.0_21]at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) ~[na:1.7.0_21]at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.7.0_21]at java.lang.reflect.Constructor.newInstance(Constructor.java:525) ~[na:1.7.0_21]at org.jboss.byteman.rule.expression.ThrowExpression.interpret(ThrowExpression.java:231) ~[na:na]at org.jboss.byteman.rule.Action.interpret(Action.java:144) ~[na:na]at org.jboss.byteman.rule.helper.InterpretedHelper.fire(InterpretedHelper.java:169) ~[na:na]at org.jboss.byteman.rule.helper.InterpretedHelper.execute0(InterpretedHelper.java:137) ~[na:na]at org.jboss.byteman.rule.helper.InterpretedHelper.execute(InterpretedHelper.java:100) ~[na:na]at org.jboss.byteman.rule.Rule.execute(Rule.java:682) ~[na:na]at org.jboss.byteman.rule.Rule.execute(Rule.java:651) ~[na:na]at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java) ~[mysql-connector-java-5.1.24.jar:na]at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:56) ~[hibernate-core-4.2.0.Final.jar:4.2.0.Final]at org.hibernate.loader.Loader.getResultSet(Loader.java:2031) [hibernate-core-4.2.0.Final.jar:4.2.0.Final]

看起來不錯,還有另一種情況:創建客戶成功但由于數據庫關閉而失敗了嗎? 這一點比較復雜,但無論如何都很容易做,讓我們看一下:

@Test( expected = CannotCreateTransactionException.class ) @BMRules(rules = {@BMRule(name="create countDown for AbstractPlainSocketImpl",targetClass = "java.net.AbstractPlainSocketImpl",targetMethod = "getOutputStream",condition = "$0.port==3306",action = "createCountDown( \"connection\", 1 )"),@BMRule(name = "throw IOException when trying to execute 2nd query to MySQL",targetClass = "java.net.AbstractPlainSocketImpl",targetMethod = "getOutputStream",condition = "$0.port==3306 && countDown( \"connection\" )",action = "throw new java.io.IOException( \"Connection refused (simulated)\" )")} ) public void testCreateCustomerAndTryToFindItWhenDatabaseIsDown() {Customer customer = customerService.create( "Customer A" );customerService.find( customer.getId() ); }

讓我解釋一下這是怎么回事。 我們希望坐在套接字級別,并且實際上控制通訊盡可能地接近網絡,而不是在JDBC驅動程序級別。 這就是為什么我們要檢測AbstractPlainSocketImpl的原因。 我們也知道MySQL的默認端口是3306,因此我們僅檢測在此端口上打開的套接字。 另一個事實,我們知道第一個創建的套接字與客戶創建相對應,我們應該讓它通過。 但是第二個對應于查找并且必須失敗。 名為“ connection”的createCountDown可以滿足以下目的:第一次調用通過(閂鎖尚未計數為零),但是第二次調用觸發MySQLTimeoutException異常。 運行此測試用例將在控制臺中打印stacktrace,并期望拋出CannotCreateTransactionException :

Caused by: java.io.IOException: Connection refused (simulated)at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.7.0_21]at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) ~[na:1.7.0_21]at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.7.0_21]at java.lang.reflect.Constructor.newInstance(Constructor.java:525) ~[na:1.7.0_21]at org.jboss.byteman.rule.expression.ThrowExpression.interpret(ThrowExpression.java:231) ~[na:na]at org.jboss.byteman.rule.Action.interpret(Action.java:144) ~[na:na]at org.jboss.byteman.rule.helper.InterpretedHelper.fire(InterpretedHelper.java:169) ~[na:na]at org.jboss.byteman.rule.helper.InterpretedHelper.execute0(InterpretedHelper.java:137) ~[na:na]at org.jboss.byteman.rule.helper.InterpretedHelper.execute(InterpretedHelper.java:100) ~[na:na]at org.jboss.byteman.rule.Rule.execute(Rule.java:682) ~[na:na]at org.jboss.byteman.rule.Rule.execute(Rule.java:651) ~[na:na]at java.net.AbstractPlainSocketImpl.getOutputStream(AbstractPlainSocketImpl.java) ~[na:1.7.0_21]at java.net.PlainSocketImpl.getOutputStream(PlainSocketImpl.java:214) ~[na:1.7.0_21]at java.net.Socket$3.run(Socket.java:915) ~[na:1.7.0_21]at java.net.Socket$3.run(Socket.java:913) ~[na:1.7.0_21]at java.security.AccessController.doPrivileged(Native Method) ~[na:1.7.0_21]at java.net.Socket.getOutputStream(Socket.java:912) ~[na:1.7.0_21]at com.mysql.jdbc.MysqlIO.(MysqlIO.java:330) ~[mysql-connector-java-5.1.24.jar:na]

大! 字節曼為不同故障模擬提供的可能性是巨大的。 仔細添加測試套件,以驗證應用程序如何對錯誤的條件做出React,可以大大提高應用程序的健壯性和對故障的適應能力。 多虧了Byteman伙計們! 請在GitHub上找到完整的項目。

參考: 使用Byteman和JUnit進行故障注入:通過Andriy Redko {devmind}博客上的JCG合作伙伴 Andrey Redko,可以做更多的工作來確保應用程序的健壯性 。

翻譯自: https://www.javacodegeeks.com/2013/04/fault-injection-with-byteman-and-junit.html

byteman

總結

以上是生活随笔為你收集整理的byteman_使用Byteman和JUnit进行故障注入的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 小视频国产| 国产又黄又骚 | 国产精品爱啪在线线免费观看 | 亚洲激情影院 | 亚洲图色在线 | 国产老妇伦国产熟女老妇视频 | 国产va在线 | 一二区在线观看 | 日本高清视频在线观看 | 日韩黄色片 | 青青青青在线 | 亚洲国产一区在线观看 | 日韩精品视频在线观看免费 | 桃色成人网 | 国产美女自慰在线观看 | 日韩欧美成人一区二区三区 | 欧美污污视频 | 色婷婷av777| 中文字幕日韩精品在线 | 日本十八禁视频无遮挡 | 久热av在线 | 免费观看一级视频 | 成年人视频免费 | 无码人妻精品丰满熟人区 | 九九热re | 波多野结衣在线网址 | www.99在线 | 国产永久视频 | 日本中文字幕第一页 | 男人的天堂黄色 | 美女被男人插 | 黄色二级毛片 | 国产91久久精品一区二区 | 亚洲精品视频在线观看免费 | 性xxxx| 青青91| 97公开免费视频 | 欧美成人福利视频 | av官网在线观看 | 热热热热色 | 亚洲逼院 | 国产99在线视频 | 小柔的裸露日记h | 男操女视频免费 | 无码不卡av东京热毛片 | 国产成人免费观看 | 香蕉国产在线 | 国产午夜精品一区二区三区嫩草 | 伊人性视频 | 操处女逼视频 | 女性女同性aⅴ免费观女性恋 | 国产成人在线视频 | 91www在线观看 | 比利时xxxx性hd极品 | 136fldh导航福利微拍 | 亚洲免费看片 | 欧美熟妇精品一区二区 | 国产手机视频在线 | 日韩中文字幕av在线 | 五月丁香综合激情六月久久 | 国产精品久久网站 | 最新免费黄色网址 | 97超碰国产精品无码蜜芽 | 91极品在线 | 亚洲综合射 | 水牛影视av一区二区免费 | 日本草草影院 | 一区二区三区四区日韩 | 台湾佬美性中文娱乐网 | 亚洲区欧美 | 欧美三区在线观看 | 精品不卡一区 | 国产成人97精品免费看片 | 天天舔天天干天天操 | 欧美三级午夜理伦 | 亚洲av无码乱码在线观看性色 | 天堂色在线 | 日韩一级黄色片 | 亚洲天堂2024| fexx性欧美| a在线一区 | 亚洲三级免费 | 欧美中文字幕第一页 | 波多野 在线 | 久久av中文字幕 | 免费福利在线视频 | 欧洲人妻丰满av无码久久不卡 | 一区二区三区四区五区av | 成人短视频在线免费观看 | 日韩少妇高潮抽搐 | 国产精品亚洲自拍 | 国产高潮呻吟久久 | 丰满熟妇被猛烈进入高清片 | 精品黄网| 色综合色婷婷 | 91丨九色 | 国产精品一区在线 | 亚洲免费观看在线 | 日韩免费看 |