私はかなり新しいプログラマーであり、テスターは言うまでもなく、私の無知を許してください。私の質問は、私の組織での API のテストに関するものです。私の仕事は、統合テストを実行して、Web アプリで使用する API 呼び出しが正しい HTTP ステータス コードを返すことを確認することです。場合によっては、API が実際にデータをデータベースに入力したことも確認しています。MSTest を使用して、Visual Studio Integrated Test Runner でテストを実行しています。目的は、毎晩ドロップとともに実行できる一連のテストを用意することです。
先に進みます....
私が使用している例では、いくつかの PatronMessage API 呼び出しを扱っています。
システムはできる
- システム定義メッセージのリストを返す
- ユーザー固有のメッセージのリストを返す
- パトロンに新しいメッセージを追加する
- パトロンのメッセージを変更する
- パトロンメッセージを削除する
私がコードを書いた方法では、他の属性の中で属性 apiObject(PatronMessages) と objectAction(GetSystemDefined-Successful) を持つ xml ファイルがありますが、これらはテスト識別の目的で使用されます。
また、いくつかの実装済み、仮想、および抽象メソッドを持つ TestCase という抽象クラスもあります。次に、TestCase を拡張し、すべての抽象を実装し、TestCase のいくつかの仮想メソッドをオーバーライドする PatronMessagesTestCase クラスを作成します。次に、TestCase の RunTest 具象メソッドに apiObject と objectAction が渡され、xml ファイルからテスト パラメーターを返してテストを実行します。この RunTest メソッドは、テストが成功したかどうかを示すブール値を返します。
私が抱えている問題は、ほとんどのテストの検証方法がまったく異なることです。PatronMessagesTestCase で TestCase の抽象メソッドを実装する MakeDatabaseCall メソッドがありますが、この MakeDatabaseCall は異なる場合と異なる場合があります。例えば:
新しいメッセージを追加するテストでは、最大のメッセージ ID のデータベース値を確認してから API 呼び出しを実行し、データベース値を確認して、呼び出し後のメッセージ ID が大きいことを確認します。削除メッセージのテストでは、同じテストを実行して、データベースの値が以前にチェックした値よりも低いことを確認します。
スイッチを追加して objectAction をオンにできることはわかっていますが、PatronMessagesTestCase 内のすべてのメソッドで objectAction をオンにする必要があり、それは間違っているように思えます。
もう一度進む…………
そこで昨日、上級開発者の 1 人に助けを求めたところ、インターフェイス (ITestCase) を使用してテストケースを構築し、ITestCase を実装するクラス (PatronAssociationsTestCase) を作成し、すべてのテストケースを実行するテスト ランナー クラスを作成することを提案されました。しかし、私はそれを少しやったので、それが私の状況にどのように役立つかについて少し混乱しました(私は本当にそれが悪化したと思います.しかし、これが私のコードです.同じことを2回達成しようとしていることに注意してください. , 2 つの異なる場所で 2 つの異なる方法で. また、これは完成したコードではないため、エラーが発生する可能性があることに注意してください (作業を開始して、自分の考えに行き詰まりました)........
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LEAPIntegrationTestingFINAL
{
public interface ITestCase
{
public Dictionary<string, object> GetTestParams(string apiObject, string objectAction);
public string ConfigureURL(string objectAction);
//public Dictionary<string, object> ConfigureRequestBody(Dictionary<string, object> testParams); //<=-=-=-Some tests need to
public Dictionary<string, object> ExecuteRequest(Dictionary<string, object> testParams);
public Dictionary<string, object> ParseResponse(Dictionary<string, object> responseValues);
//public Dictionary<string, object> MakeDatabaseCall(Dictionary<string, object> testParams); //<=-=-=-Some tests need to
public bool CompareData(Dictionary<string, object> databaseValues, Dictionary<string, object> responseValues);
}
public static class TestRunner
{
public static bool RunTest(ITestCase TestCase, string apiObject, string objectAction)
{
bool comparisonValue = false;
return comparisonValue;
}
}
public class PatronAssociationsTestCase : ITestCase
{
public Dictionary<string, object> GetTestParams(string apiObject, string objectAction)
{
throw new NotImplementedException();
}
public string ConfigureURL(string objectAction)
{
throw new NotImplementedException();
}
public Dictionary<string, object> ConfigureRequestBody(Dictionary<string, object> testParams)
{
throw new NotImplementedException();
}
public Dictionary<string, object> ExecuteRequest(Dictionary<string, object> testParams)
{
throw new NotImplementedException();
}
public Dictionary<string, object> ParseResponse(Dictionary<string, object> responseValues)
{
throw new NotImplementedException();
}
public Dictionary<string, object> MakeDatabaseCall(Dictionary<string, object> testParams)
{
throw new NotImplementedException();
}
public bool CompareData(Dictionary<string, object> databaseValues, Dictionary<string, object> responseValues)
{
throw new NotImplementedException();
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<TestParameterFile>
<Messages action="GETSystemDefined-Succesful"
method="GET" expectedReponseCode ="200"/>
<Messages action="AddPatronMessage-InvalidPatronID"
method = "POST" expResponseCode="404" requestBody="{"MessageType": 101,"MessageValue": "This is a free text message for this patron!"}"/>
</TestParameterFile>
これは私が実装しようとした元の方法です(私はより良い方法だと思います)可能であればインターフェースを組み込みたいです
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using System.Data.SqlClient;
namespace LEAPIntegrationTestingFINAL
{
public abstract class TestCase
{
public static string authorizationString
{
get
{
string statusCode;
AppConfig.Change("D:\\SampleProjects\\LEAP\\LEAPIntegrationTestingFINAL\\LEAPIntegrationTestingFINAL\\TestingSuiteConfiguration.config");
//Converting User credentials to base 64.
string uri = ConfigurationManager.AppSettings["authURL"];
string uname = ConfigurationManager.AppSettings["username"];
string pword = ConfigurationManager.AppSettings["password"];
string _auth = string.Format("{0}:{1}", uname, pword);
string _enc = Convert.ToBase64String(Encoding.ASCII.GetBytes(_auth));
string _cred = string.Format("{0} {1}", "Basic", _enc);
WebRequest authRequest = WebRequest.Create(uri);
authRequest.Headers.Add(HttpRequestHeader.Authorization, _cred);
authRequest.ContentLength = 0;
authRequest.Method = "POST";
//Creating and receiving WebResponse
HttpWebResponse authResponse = (HttpWebResponse)authRequest.GetResponse();
statusCode = Convert.ToString((int)authResponse.StatusCode);
using (Stream responseStream = authResponse.GetResponseStream())
{
using (StreamReader sr = new StreamReader(responseStream))
{
Dictionary<string, object> authResponseObject = new Dictionary<string, object>();
string authJSON = sr.ReadLine();
sr.Close();
responseStream.Close();
//Deserialization of jSON object and creating authorization String
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
authResponseObject = jsSerializer.Deserialize<Dictionary<string, object>>(authJSON);
string AccessToken = authResponseObject["AccessToken"].ToString();
string AccessSecret = authResponseObject["AccessSecret"].ToString();
string authorizationString = "Authorization: PAS " + AccessToken + ":" + AccessSecret;
return authorizationString;
}
}
}
}
public string url { get; set; }
public Dictionary<string, object> testParams { get; set; }
public Dictionary<string, object> responseValues { get; set; }
public Dictionary<string, object> databaseValues { get; set; }
public bool comparisonValue { get; set; }
public bool RunTest(string apiObject, string objectAction)
{
bool comparisonValue = false;
this.testParams = GetTestParams(apiObject, objectAction);
this.url = RootURLBuilder.Build();
this.url = ConfigureURL(this.url);
#region ConfigRequestBody
if (this.testParams.ContainsKey("requestBody"))
{
this.testParams = ConfigureRequestBody(this.testParams);
}
#endregion
this.responseValues = ExecuteRequest(this.testParams);
this.responseValues = ParseResponse(this.responseValues);
#region MakeDatabaseCall
if (this.testParams.ContainsKey("query"))
{
this.databaseValues = MakeDatabaseCall(this.testParams);
}
#endregion
this.comparisonValue = CompareData(this.databaseValues, this.responseValues);
return comparisonValue;
}
private Dictionary<string, object> GetTestParams(string apiObject, string objectAction)
{
Dictionary<string, object> testParams = new Dictionary<string, object>();
return testParams;
}
public abstract string ConfigureURL(string objectAction);
public abstract Dictionary<string, object> ConfigureRequestBody(Dictionary<string,object> testParams);
public Dictionary<string, object> ExecuteRequest(Dictionary<string, object> testParams)
{
Dictionary<string, object> responseValues = new Dictionary<string, object>();
string requestBody = null;
string httpVerb = this.testParams["method"].ToString();
#region requestBody = this.testParams["requestBody"].ToString();
if (this.testParams.ContainsKey("requestBody"))
{
requestBody = this.testParams["requestBody"].ToString();
}
#endregion
WebRequest req = WebRequest.Create(this.url);
req.Headers.Add(authorizationString);
req.ContentType = "application/json";
req.Method = httpVerb;
if (requestBody != null)
{
req.ContentLength = requestBody.Length;
using (Stream reqStream = req.GetRequestStream())
{
byte[] reqBodyBytes = Encoding.UTF8.GetBytes(requestBody);
reqStream.Write(reqBodyBytes, 0, reqBodyBytes.Length);
}
}
else
req.ContentLength = 0;
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
string responseJSON;
int statusCode = (int)resp.StatusCode;
string statusMessage = resp.StatusDescription;
Stream respStream = resp.GetResponseStream();
StreamReader sr = new StreamReader(respStream);
responseJSON = sr.ReadToEnd();
respStream.Close();
sr.Close();
responseValues.Add("statusCode", statusCode);
responseValues.Add("statusMessage", statusMessage);
responseValues.Add("responseJSON", responseJSON);
}
return responseValues;
}
public virtual Dictionary<string, object> ParseResponse(Dictionary<string, object> responseValues)
{
Dictionary<string, object> parsedResponseJSON = new Dictionary<string, object>();
Dictionary<string, object> parsedResponse = new Dictionary<string, object>();
string json = this.responseValues["responseJSON"].ToString();
parsedResponse.Add("statusCode", this.responseValues["statusCode"]);
parsedResponse.Add("statusMessage", this.responseValues["statusMessage"]);
return parsedResponse;
}
public abstract Dictionary<string, object> MakeDatabaseCall(Dictionary<string, object> testParams);
public abstract bool CompareData(Dictionary<string, object> databaseValues, Dictionary<string, object> responseValues);
}
public class PatronMessageTestCase : TestCase
{
public override string ConfigureURL(string objectAction)
{
string url;
switch (objectAction)
{
case "GETSystemDefined-Succesful":
url = this.url + "/patronmessages";
return url;
default:
return "Not Found";
}
}
public override Dictionary<string, object> ConfigureRequestBody(Dictionary<string, object> testParams)
{
throw new NotImplementedException();
}
public override Dictionary<string, object> MakeDatabaseCall(Dictionary<string, object> testParams)
{
throw new NotImplementedException();
}
public override bool CompareData(Dictionary<string, object> databaseValues, Dictionary<string, object> responseValues)
{
throw new NotImplementedException();
}
}
}
最後に..... テストを実行し、MSTest を使用してそれを制御するファイル
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace LEAPIntegrationTestingFINAL
{
[TestClass]
public class LEAPTestingSuite
{
[TestMethod]
public void MessagesTests()
{
PatronMessageTestCase MessagesTests = new PatronMessageTestCase();
Assert.AreEqual(true, MessagesTests.RunTest("Messages", "GETSystemDefined-Succesful"));
}
[TestMethod]
public void AssociationsTests()
{
PatronAssociationsTestCase GETSystemDefined = new PatronAssociationsTestCase();
Assert.AreEqual(true, TestRunner.RunTest());
}
}
}
他にもいくつかのクラスがあり、それらのいずれかが必要な場合はお知らせください...そして要約すると、私の主な目標は、API 呼び出しを行い、返された応答コードが xml ファイルの expectedResponseCode と一致することを確認することです。また、オプションでデータベース呼び出しを行い、データベースの値が API 呼び出しから返されたデータと一致することを確認することもできます。また、同僚はインターフェースの使用を提案しました