如何解决使用 Moq 进行 Elasticsearch Nest 单元测试:无法模拟具体类
我需要在我的存储库中使用的 nest.IElasticclient
接口中模拟一些方法和属性。
例如,我需要存根 Indices.Exists()
方法以返回一个 ExistsResponse
,其 Exists
属性返回 true。
问题在于具体类没有接口实现,也没有 Exists 属性上的 setter,并且在 nest lib 中也没有声明为 virtual:
public class ExistsResponse : ResponseBase
{
public ExistsResponse();
public bool Exists { get; }
}
public ExistsResponse Exists(Indices index,Func<IndexExistsDescriptor,IIndexExistsRequest> selector = null);
因此,对于模拟,我无论如何都尝试在具体类上设置属性,但是以下所有方法都失败了,我不知道该怎么做...
/* Fail with exception :
System.NotSupportedException : Unsupported expression: x => x.Exists
Non-overridable members (here: ExistsResponse.get_Exists) may not be used in setup / verification expressions.
*/
var mock1 = new Mock<ExistsResponse>();
obj.SetupGet(f => f.Exists).Returns(true);
/* Fail with exception :
System.NotSupportedException : Unsupported expression: f => f.Exists
Non-overridable members (here: ExistsResponse.get_Exists) may not be used in setup / verification expressions.
*/
var mock2 = Mock.Of<ExistsResponse>(x => x.Exists == true);
/* Fail with exception :
System.ArgumentException : Property set method not found.
*/
var mock3 = new ExistsResponse();
var property = typeof(ExistsResponse).GetProperty("Exists",BindingFlags.Public | BindingFlags.Instance);
property.SetValue(mock3,true);
/* Fail with exception :
System.NullReferenceException (setter is null)
*/
var mock4 = new ExistsResponse();
var setter = property.GetSetMethod(true);
setter.Invoke(mock4,new object[] { true });
// My Mock on the Indices.Exists method
var elasticmock = new Mock<IElasticclient>();
elasticmock
.Setup(x => x.Indices.Exists(It.IsAny<string>(),null))
.Returns(/*** my stubbed object here ***/); // <== how to stub the concrete class to return a ExistsResponse.Exists = true ?
我的库:
nest 7.12.1
Moq 4.15.2
XUnit 2.4.1
.Net 5
感谢您的帮助
解决方法
模拟你不拥有的东西通常不好,但如果你想模拟弹性客户端响应,最好的方法是使用 InMemryConnection,你可以像这样用 InMemryConnection 实例化一个新的 elasticClient:
InMemory 弹性客户端:
var connection = new InMemoryConnection();
var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(connectionPool,connection);
var client = new ElasticClient(settings);
然后你需要伪造响应并要求客户端返回它,如下所示:
var response = new
{
took = 1,timed_out = false,_shards = new
{
total = 2,successful = 2,failed = 0
},hits = new
{
total = new { value = 25 },max_score = 1.0,hits = Enumerable.Range(1,25).Select(i => (object)new
{
_index = "project",_type = "project",_id = $"Project {i}",_score = 1.0,_source = new { name = $"Project {i}" }
}).ToArray()
}
};
var responseBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(response));
var connection = new InMemoryConnection(responseBytes,200);
var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(connectionPool,connection).DefaultIndex("project");
var client = new ElasticClient(settings);
var searchResponse = client.Search<Project>(s => s.MatchAll());
您可以找到有关 InMemoryConnection here 的更多信息。
如果您想正确地遵循不要嘲笑您不拥有的东西,您需要执行以下步骤:
- 将
ElasticClient
和 ElasticClient 的所有Request
和Responses
包裹在这个库中,然后您可以轻松模拟您需要的内容。 - 您应该使用集成测试来测试您的包装类,以确保您的包装类按预期工作。
更新:
对于模拟 Indices.Exists
,您只需发送 200 OK,因此您只需执行以下操作:
var connection = new InMemoryConnection(responseBytes,200);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。