Home  >  Article  >  Java  >  Detailed explanation of switching between multiple data sources in Java automated testing

Detailed explanation of switching between multiple data sources in Java automated testing

黄舟
黄舟Original
2017-10-09 10:18:251981browse

The following editor will bring you an article on switching between multiple data sources in Java automated testing (explanation with examples). The editor thinks it’s pretty good, so I’ll share it with you now and give it as a reference. Let’s follow the editor and take a look.

When doing automated testing, data-driven is a very important concept. When the data is separated from the script, facing the vast amount of data, managing the data becomes a big problem. Problem, and data sources may face multiple ones. Just like during the development process, sometimes you need to connect to MYSQL and sometimes you need to connect to SQL SERVER. How to switch quickly? In the following example, we will start with a data source and demonstrate it step by step:

1. The basic method of writing data-driven using external files

1.1 When we are data-driven, we store the data in the JAVA property file: data.properties


username=test
password=123456

1.2 Parse the properties file


public class PropertiesHandler {
 
 private static Properties loadPropertiesFile(String filePath){
  Properties p = new Properties();
  InputStream in = null;
  try {
   in = new FileInputStream(new File(filePath));
   p.load(in);
  } catch (FileNotFoundException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }finally{
   try {
    if(in != null){
     in.close();
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
  return p;
 }
  
 /**
  * 将property转换成Map
  * @param key
  * @return
  */
 @SuppressWarnings({ "rawtypes", "unchecked" })
 public static Map<String, String> getPropertyData(String filePath){
  try{
   return new HashMap<String, String>((Map)PropertiesHandler.loadPropertiesFile(filePath));
  }catch(Exception e){
   e.printStackTrace();
  }
  return new HashMap<String, String>();
 }
  
 public static void main(String[] args) {
  System.out.println(PropertiesHandler.getPropertyData("file/data.properties"));
 }
}

1.3 Write a TestBase class, which is used DataProvider that stores TestNg


##

public class TestBase {
  
 @DataProvider
 public Object[][] dataProvider(){
  return this.getTestData();
 }
  
 private Object[][] getTestData(){
  PropertiesData testData = new PropertiesData();
  List<Map<String, String>> listData = testData.getTestMethodData();
  Object[][] object = new Object[listData.size()][];
  for (int i = 0; i < listData.size(); i++) {
   object[i] = new Object[]{listData.get(i)};
  }
  return object;
 }
  
}

It can be seen that as long as I have a class, I can provide a data type: List9a9bf6e7320d2fa9556654d94c7e96dc> can be converted into a two-dimensional array of Object[][], which can be provided to the test method to run.

1.4 A PropertiesData class appeared in 1.3, now let’s implement this class

##

public class PropertiesData {
  
 public List<Map<String, String>> getTestMethodData(){
  List<Map<String, String>> list = new ArrayList<Map<String, String>>();
  list.add(PropertiesHandler.getPropertyData("file/data.properties"));
  return list;
 }
  
}

1.5 There are data parsing classes, data loading classes, and basic classes for data provision among the above, so we combine the testing methods to integrate these three basic classes, and we have A complete example of an external file as a data source is formed:

public class TestDemo extends TestBase{
  
 @Test(dataProvider="dataProvider")
 public void testDemo(Map<String, String> param){
  System.out.println(param.get("username"));
  System.out.println(param.get("password"));
 }
  
}

2. Implementation of replacing the property file with a txt file

2.1 If there are multiple data sources, I want to use txt to store the data. A json string is stored in txt: data.txt

{
 "username":"test",
 "password":"123456"
}

2.2 Read out this txt file

public class FileUtils {
 
 public static String readFile(String fileName) {
  InputStream is = null;
  StringBuffer sb = new StringBuffer();
  try {
   is = new FileInputStream(fileName);
   byte[] byteBuffer = new byte[is.available()];
   int read = 0;
   while((read = is.read(byteBuffer)) != -1){
    sb.append(new String(byteBuffer, 0, read));
   }
  } catch (FileNotFoundException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }finally{
   try {
    if(is!=null){
     is.close();
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
  return sb.toString();
 }
  
 public static void main(String[] args) {
  System.out.println(FileUtils.readFile("file/data.txt"));
 }
  
}

2.3 Parse the read JSON string (you need to use a JAR package here, gson.jar)

public class TxtData {
  
 public List<Map<String, String>> getTestMethodData(){
  List<Map<String, String>> list = new ArrayList<Map<String, String>>();
  String data = FileUtils.readFile("file/data.txt");
  Gson gson = new Gson();
  Map<String, String> dataMap = gson.fromJson(data, new TypeToken<Map<String, String>>(){}.getType());
  list.add(dataMap);
  return list;
 }
  
}

2.4 Use the TxtData class, that is, replace the propertiesData class in the TestBase class with the TxtData class

private Object[][] getTestData(){
 TxtData testData = new TxtData();
 List<Map<String, String>> listData = testData.getTestMethodData();
 Object[][] object = new Object[listData.size()][];
 for (int i = 0; i < listData.size(); i++) {
  object[i] = new Object[]{listData.get(i)};
 }
 return object;
}

2.5 After running the TestDemo test class, it is found that the results are exactly the same as those that appeared before using the PropertiesData class.

3. Use interfaces to implement 3.1 For the above two data sources, add the After the content is loaded and the loaded data type is: Listfe497e2be29e556eda152481563466e6>, we only need to replace the data source loading class in the TestBase class. In this way, we can use JAVA To reconstruct our code using the interface inside, of course we must first have an interface

public interface DataInterface {
 public List<Map<String, String>> getTestMethodData();
}

3.2 Our PropertiesData class and TxtData class must of course implement this interface

public class PropertiesData implements DataInterface{
  
 public List<Map<String, String>> getTestMethodData(){
  List<Map<String, String>> list = new ArrayList<Map<String, String>>();
  list.add(PropertiesHandler.getPropertyData("file/data.properties"));
  return list;
 }
  
}

public class TxtData implements DataInterface{
  
 public List<Map<String, String>> getTestMethodData(){
  List<Map<String, String>> list = new ArrayList<Map<String, String>>();
  String data = FileUtils.readFile("file/data.txt");
  Gson gson = new Gson();
  Map<String, String> dataMap = gson.fromJson(data, new TypeToken<Map<String, String>>(){}.getType());
  list.add(dataMap);
  return list;
 }
  
}

3.3 Then something will change in TestBase, that is, the class object that generates data loading will change. We are in TestBase Add a new method (this is a new way to generate class objects)

private DataInterface getDataInstance(String key){
 DataInterface data = null;
 try {
  data = (DataInterface) Class.forName(key).newInstance();
 } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
  e.printStackTrace();
 }
 return data;
}

3.4 The getTestData() method in the TestBase class needs to be changed again

private Object[][] getTestData(){
 DataInterface testData = this.getDataInstance("com.test.testdata.PropertiesData");
 List<Map<String, String>> listData = testData.getTestMethodData();
 Object[][] object = new Object[listData.size()][];
 for (int i = 0; i < listData.size(); i++) {
  object[i] = new Object[]{listData.get(i)};
 }
 return object;
}

private Object[][] getTestData(){
 DataInterface testData = this.getDataInstance("com.test.testdata.TxtData");
 List<Map<String, String>> listData = testData.getTestMethodData();
 Object[][] object = new Object[listData.size()][];
 for (int i = 0; i < listData.size(); i++) {
  object[i] = new Object[]{listData.get(i)};
 }
 return object;
}

3.5 Run TestDemo again and you will find that the result is still the same. Therefore, at this time you only need to change the path of the data loading class.

4. Configurable path of data loading class4.1 At this time, we can think about the path of data loading class Written in the configuration file config.properties

DataSource=com.test.testdata.TxtData

4.2 Load the config file

public class Config {
  
 public static String DATA_SOURCE;
  
 static{
  Map<String, String> map = PropertiesHandler.getPropertyData("config/config.properties");
  DATA_SOURCE = map.get("DataSource");
 }
  
}

4.3 Change the The getTestData() method is improved again:

private Object[][] getTestData(){
 DataInterface testData = this.getDataInstance(Config.DATA_SOURCE);
 List<Map<String, String>> listData = testData.getTestMethodData();
 Object[][] object = new Object[listData.size()][];
 for (int i = 0; i < listData.size(); i++) {
  object[i] = new Object[]{listData.get(i)};
 }
 return object;
}

4.4 Run the TestDemo class again, and the result is still the same. So far, we have implemented changing the content in the configuration file to select the data source to load.

5. Switching between multiple data sources

5.1 如果一个测试类里有两个测试方法,那么在配置文件里配置好数据源后,就表示这两个测试方法都将会加载同样的数据源,但如果我们希望一个测试方法用属性文件的数据源,另一个方法用TXT的数据源,这个如何办?也就是需要实现在全局配置化后,局部可再次选择数据源。我将会利用到JAVA里的注解,来实现。所以我们先定义一个DataSource的注解


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
 String value();
}

5.2 解析该注解


public class DataSources {
  
 public static String getDataSource(Method method){
  DataSource ds = method.getAnnotation(DataSource.class);
  if(ds != null){
   return ds.value();
  }
  return null;
 }
  
}

5.3 该注解的使用


@DataSource("com.test.testdata.PropertiesData")
@Test(dataProvider="dataProvider")
public void testDemo(Map<String, String> param){
 System.out.println(param.get("username"));
 System.out.println(param.get("password"));
}

5.4 TestBase类里的getTestData()方法再次的更改,要利用上这个注解解析出来的值


private Object[][] getTestData(Method method){
 String sourceKey = DataSources.getDataSource(method);
 if(sourceKey==null){
  sourceKey = Config.DATA_SOURCE;
 }
 DataInterface testData = this.getDataInstance(sourceKey);
 List<Map<String, String>> listData = testData.getTestMethodData();
 Object[][] object = new Object[listData.size()][];
 for (int i = 0; i < listData.size(); i++) {
  object[i] = new Object[]{listData.get(i)};
 }
 return object;
}

这段代码可以看到,如果测试方法标注DataSource,则会以标注的注解值为准,否则则会以全局配置的值为准。

5.5 在TestDemo里多加一个测试方法,以示区别


public class TestDemo extends TestBase{
  
 @DataSource("com.test.testdata.PropertiesData")
 @Test(dataProvider="dataProvider")
 public void testDemo(Map<String, String> param){
  System.out.println(param.get("username"));
  System.out.println(param.get("password"));
 }
  
 @Test(dataProvider="dataProvider")
 public void testDemo1(Map<String, String> param){
  System.out.println(param.get("username"));
  System.out.println(param.get("password"));
 }
  
}

上面的测试类中,两个测试方法,一个用了全局的配置数据源值,一个用了注解数据源值。大家可以运行的看看结果。

六. 工程结构图:

The above is the detailed content of Detailed explanation of switching between multiple data sources in Java automated testing. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn