C# Azure持久HTTPStart方法中的单元测试(Rhino)DBUp
技术堆栈C# Azure持久HTTPStart方法中的单元测试(Rhino)DBUp,c#,unit-testing,rhino-mocks,azure-durable-functions,dbup,C#,Unit Testing,Rhino Mocks,Azure Durable Functions,Dbup,技术堆栈 数据库升级的DBUP 用于活动的Azure耐久性 Rhino模拟用于单元测试 形势 目前,我已将DB Upgrade(DBUp)语句放在HTTPStart方法中,作为持久azure函数的入口点 DeployChanges.To .SqlDatabase(connectionString) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()) .LogToConsole() .Build(); 问题 这种方法的
DeployChanges.To
.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
.LogToConsole()
.Build();
问题
这种方法的问题是DBUp使用静态类来升级DB,而我不能使用Rhino来模拟静态类上的方法
问题
我曾想过将DBUp部分封装在一个非静态类中,但随后需要模拟构造函数初始化。不确定这是否有效
代码-升级DB的助手类
public class DBUPHelper
{
public bool UpgradeDB()
{
bool status = true;
var connectionString = "Data Source=localhost;Initial Catalog=master;Integrated Security=True;Connect Timeout=15";
var upgrader =
DeployChanges.To
.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
.LogToConsole()
.Build();
var result = upgrader.PerformUpgrade();
if (!result.Successful)
{
status = false;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(result.Error);
Console.ResetColor();
}
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Success!");
Console.ResetColor();
return status;
}
}
代码-调用Helper类的HTTPStart方法
private static ILogger logObj;
[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClientBase starter,
string functionName,
ILogger log, ExecutionContext context)
{
HttpResponseMessage response = null;
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Helper.Helper helper = new Helper.Helper(config.GetConnectionString("ConnString"););
if (helper.UpgradeDB())
{
log.LogInformation("DB Upgraded Successfully");
logObj = log;
try
{
var provider = new MultipartMemoryStreamProvider();
await req.Content.ReadAsMultipartAsync(provider);
Application policy = await GeneratePolicyObject(provider);
string instanceId = await starter.StartNewAsync(functionName, policy);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
response = starter.CreateCheckStatusResponse(req, instanceId);
response.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(10));
}
catch (Exception ex)
{
response = new HttpResponseMessage();
log.LogCritical(ex.ToString());
log.LogCritical(ex.InnerException.ToString());
log.LogCritical(ex.StackTrace);
response.Content = new StringContent(ex.ToString());
response.StatusCode = System.Net.HttpStatusCode.InternalServerError;
}
}
else log.LogCritical("DB Upgrade Failed. Check logs for exception");
return response;
}
私有静态ILogger logObj;
[函数名(“HttpStart”)]
公共静态异步任务运行(
[HttpTrigger(AuthorizationLevel.Function,methods:“post”,Route=“orchestrators/{functionName}”)]HttpRequestMessage请求,
[OrchestrationClient]DurableOrchestrationClientBase启动器,
字符串函数名,
ILogger日志,ExecutionContext(上下文)
{
HttpResponseMessage响应=null;
var config=new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile(“local.settings.json”,可选:true,重载更改:true)
.AddenEnvironmentVariables()
.Build();
Helper.Helper Helper=new Helper.Helper(config.GetConnectionString(“ConnString”););
if(helper.UpgradeDB())
{
日志信息(“数据库升级成功”);
logObj=log;
尝试
{
var provider=新的MultipartMemoryStreamProvider();
wait req.Content.ReadAsMultipartAsync(提供程序);
应用程序策略=等待生成策略对象(提供程序);
字符串instanceId=await starter.StartNewAsync(函数名,策略);
LogInformation($“启动了ID为“{instanceId}”的业务流程”;
response=starter.CreateCheckStatusResponse(请求,实例ID);
response.Headers.RetryAfter=新的RetryConditionHeaderValue(TimeSpan.FromSeconds(10));
}
捕获(例外情况除外)
{
响应=新的HttpResponseMessage();
LogCritical(例如ToString());
LogCritical(例如InnerException.ToString());
log.LogCritical(例如StackTrace);
response.Content=newstringcontent(例如ToString());
response.StatusCode=System.Net.HttpStatusCode.InternalServerError;
}
}
else log.LogCritical(“数据库升级失败,请检查日志是否存在异常”);
返回响应;
}
请参见突出显示的区域。我想模拟构造函数初始化,以便在单元测试时不会发生DB调用
有人能帮忙吗
关于Tarun使用抽象来避免与实现问题的紧密耦合
public interface IDBHelper {
bool UpgradeDB();
}
public class DBUPHelper: IDBHelper {
//...code omitted for brevity
}
此外,由于测试中的方法是静态的,因此会暴露静态字段/属性
public static class MyFunction {
//At run time this will use default helper
public static IDBHelper Helper = new DBUPHelper();
private static ILogger logObj;
[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClientBase starter,
string functionName,
ILogger log, ExecutionContext context)
{
HttpResponseMessage response = null;
if (helper.UpgradeDB()) {
log.LogInformation("DB Upgraded Successfully");
logObj = log;
try
{
var provider = new MultipartMemoryStreamProvider();
await req.Content.ReadAsMultipartAsync(provider);
Application policy = await GeneratePolicyObject(provider);
string instanceId = await starter.StartNewAsync(functionName, policy);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
response = starter.CreateCheckStatusResponse(req, instanceId);
response.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(10));
}
catch (Exception ex)
{
response = new HttpResponseMessage();
log.LogCritical(ex.ToString());
log.LogCritical(ex.InnerException.ToString());
log.LogCritical(ex.StackTrace);
response.Content = new StringContent(ex.ToString());
response.StatusCode = System.Net.HttpStatusCode.InternalServerError;
}
}
else log.LogCritical("DB Upgrade Failed. Check logs for exception");
return response;
}
}
公共静态类MyFunction{
//在运行时,这将使用默认的帮助程序
public static IDBHelper Helper=new dbupulper();
专用静态ILogger logObj;
[函数名(“HttpStart”)]
公共静态异步任务运行(
[HttpTrigger(AuthorizationLevel.Function,methods:“post”,Route=“orchestrators/{functionName}”)]HttpRequestMessage请求,
[OrchestrationClient]DurableOrchestrationClientBase启动器,
字符串函数名,
ILogger日志,ExecutionContext(上下文)
{
HttpResponseMessage响应=null;
if(helper.UpgradeDB()){
日志信息(“数据库升级成功”);
logObj=log;
尝试
{
var provider=新的MultipartMemoryStreamProvider();
wait req.Content.ReadAsMultipartAsync(提供程序);
应用程序策略=等待生成策略对象(提供程序);
字符串instanceId=await starter.StartNewAsync(函数名,策略);
LogInformation($“启动了ID为“{instanceId}”的业务流程”;
response=starter.CreateCheckStatusResponse(请求,实例ID);
response.Headers.RetryAfter=新的RetryConditionHeaderValue(TimeSpan.FromSeconds(10));
}
捕获(例外情况除外)
{
响应=新的HttpResponseMessage();
LogCritical(例如ToString());
LogCritical(例如InnerException.ToString());
log.LogCritical(例如StackTrace);
response.Content=newstringcontent(例如ToString());
response.StatusCode=System.Net.HttpStatusCode.InternalServerError;
}
}
else log.LogCritical(“数据库升级失败,请检查日志是否存在异常”);
返回响应;
}
}
可在隔离测试时替换
public async Task TestFunction {
//Arrange
var helper = MockRepository.GenerateMock<IDBHelper>();
MyFunction.helper = helper; //<<--override default helper with mock
helper.Stub(_ => _.UpgradeDB()).Return(false);//or true is that is what you desire
//...arrange other parameters / dependencies
//Act
var actual = await MyFunction.Run(...);
//Assert
//...
}
公共异步任务TestFunction{
//安排
var helper=MockRepository.GenerateMock();
MyFunction.helper=helper;//如果您希望能够独立地模拟和测试,那么非静态是一个不错的选择。在感谢您的响应@Nkosi中,通过模拟构造函数初始化来说明您的意思。感谢您的帮助。我添加了代码片段供参考。请注意,我对Rhino和单元测试的模拟策略并不熟悉e代码而不是图像当然,我包括了图像,这样我可以突出显示该区域。无论如何,我编辑了这个问题。解释得非常好。非常感谢。这将帮助我在单元测试中使用其他方法