我正在尝试通过单元测试来分析代码覆盖率,我目前正在使用Moq库执行单元测试,以某种方式我走错了路,想知道以下情况是否适用于Moq
下面是一段代码
public interface ISearchWorker
{
void DeleteIndex(string indexName);
T GetClient<T>();
}
public class ElasticSearchWorker : ISearchWorker
{
public void DeleteIndex(string indexName)
{
IElasticClient elasticClient = GetClient<IElasticClient>();
if (elasticClient.IndexExists(indexName).Exists)
{
_ = elasticClient.DeleteIndex(indexName);
}
}
public T GetClient<T>()
{
string nodeList = "http://localhost:9200/";
List<Node> nodes = nodeList.Split(',').Select(uri => new Node(new Uri(uri))).ToList();
IConnectionPool sniffingConnectionPool = new SniffingConnectionPool(nodes);
IConnectionSettingsValues connectionSettings = new ConnectionSettings(sniffingConnectionPool);
return (T)(IElasticClient)new ElasticClient(connectionSettings);
}
}
下面是单元测试的代码段
[TestClass]
public class SearchTestClass
{
private ISearchWorker searchWorker;
private Mock<ISearchWorker> searchWorkerMoq;
private readonly string indexName = "testIndex";
[TestInitialize]
public void SetupElasticClient()
{
searchWorkerMoq = new Mock<ISearchWorker>();
var elasticClient = new Mock<IElasticClient>();
searchWorkerMoq.Setup(c => c.GetClient<IElasticClient>()).Returns(elasticClient.Object).Verifiable();
searchWorker = searchWorkerMoq.Object;
}
[TestMethod]
public void DeleteIndexTest()
{
try
{
searchWorker.DeleteIndex(indexName);
searchWorkerMoq.Verify(c => c.GetClient<IElasticClient>(), Times.Once());
}
catch (System.Exception)
{
throw;
}
}
}
行
searchWorkerMoq.Verify(c => c.GetClient<IElasticClient>(), Times.Once());
引发以下异常
(Moq.MockException: '
Expected invocation on the mock once, but was 0 times: c => c.GetClient<IElasticClient>())
通读大多数与Moq相关的信息,似乎这不是执行Moq测试的适当方法,应该将IElasticClient对象从外部提供给ElasticSearchWorker类
不通过外部注入提供IElasticClient对象的原因是,我们计划为另一个搜索提供程序(Azure Search)实现ISearchWorker,因此希望将客户端实体保留在实现ISearchWorker接口的类之内
想知道是否有更好的方法来执行此测试,以及在这种情况下我们将如何实现代码覆盖。
答案 0 :(得分:1)
所以我认为您了解为什么这不起作用,而您只是在寻求“好的方法”来解决此问题。
现在我并不是说这是最好的方法,但是在保持“干净”的同时,这可能是最快的方法。
应用接口隔离(来自SOLID的“ I”)。创建两个接口,而不是一个,然后在以后的阶段将它们都实现。
// Don't have a C# IDE with me, so sorry if I leave some syntax errors.
public interface ISearchClientProvider
{
T GetClient<T>();
}
public interface ISearchWorker
{
void DeleteIndex(string indexName);
}
public class ElasticSearchWorker : ISearchWorker{
private readonly ISearchClientProvider _clientProvider;
public ElasticSearchWorker(ISearchClientProvider clientProvider){
_clientProvider = clientProvider;
}
public void DeleteIndex(string indexName)
{
var elasticClient = _clientProvider.GetClient<IElasticClient>();
if (elasticClient.IndexExists(indexName).Exists)
{
_ = elasticClient.DeleteIndex(indexName);
}
}
}
public class ElasticSearchClientProvider : ISearchClientProvider{/*some implementation*/}
public class AzureSearchWorker : ISearchWorker{/*some implementation*/}
public class AzureSearchClientProvider : ISearchClientProvider{/*some implementation*/}
然后测试代码应类似于:
// would actually prefer to name it ElasticSearchWorkerTests
[TestClass]
public class SearchTestClass
{
private readonly ElasticSearchWorker _searchWorker;
private readonly ISearchClientProvider _elasticClientProvider;
private readonly string indexName = "testIndex";
// would prefer to name it SetupElasticSearchWorker
[TestInitialize]
public void SetupElasticClient()
{
var elasticClient = new Mock<IElasticClient>();
// Setup for IElasticClient.IndexExists() function:
// I don't know what is the return type of IndexExists,
// so I am assuming here that it is some dynamic Object
elasticClient.Setup(c => c.IndexExists(indexName)).Returns(new {Exists = true});
// Setup for IElasticCleint.DeleteIndex might also be necessary here.
_elasticClientProvider = new Mock<ISearchClientProvider>();
_elasticClientProvider.Setup(c => c.GetClient<IElasticClient>()).Returns(elasticClient.Object).Verifiable();
_searchWorker = new ElasticSearchWorker(_elasticClientProvider);
}
// would prefer to name it DeleteIndexTest_GetsSearchClient,
// because the function does more than is checked here, e.g., Checks index, deletes index.
[TestMethod]
public void DeleteIndexTest()
{
try
{
searchWorker.DeleteIndex(indexName);
searchWorkerMoq.Verify(c => c.GetClient<IElasticClient>(), Times.Once());
}
catch (System.Exception)
{
throw;
}
}
}
这样,此测试中将不会有http请求。
通常(可以争论的部分),如果您希望代码可以进行单元测试: