如何解决使用 NSubstitute 使用内部 SDK 调用模拟类
第一次尝试使用 NSubstitute。
我的 Web API 中有以下方法。 对于那些不了解 Couchbase 的人,可以说集合/存储桶就像一个数据库表,一个键就像一个数据库行。
Couchbase_internal.Collection_GET 返回 Task<ICouchbaseCollection>
我想编写 2 个单元测试。
当键存在时测试返回的类,当键不存在时测试返回的类 (couchbaseServiceResultClass)。
我真的不明白我控制模拟数据中是否存在密钥的部分。
public class CouchbaseAPI : ControllerBase,ICouchbaseAPI
{
// GET /document_GET?bucketName=<bucketName>&key=<key>
[HttpGet]
[Consumes("application/x-www-form-urlencoded")]
[Produces(MediaTypeNames.Application.Json)]
public async Task<couchbaseServiceResultClass> document_GET([FromQuery,Bindrequired] string bucketName,[FromQuery,Bindrequired] string key)
{
var collection = await Couchbase_internal.Collection_GET(bucketName);
if (collection != null)
{
IGetResult result;
try
{
// get document
result = await collection.GetAsync(key);
}
catch (CouchbaseException ex)
{
return new ErrorHandling().handleCouchbaseException(ex);
}
couchbaseServiceResultClass decryptResult = new();
try
{
// decrypt document
decryptResult = Encryption.decryptContent(result);
}
catch (Exception ex)
{
return new ErrorHandling().handleException(ex,null);
}
// remove document if decryption Failed
if (!decryptResult.DecryptSuccess)
{
try
{
await collection.RemoveAsync(key);
}
catch (CouchbaseException ex)
{
return new ErrorHandling().handleCouchbaseException(ex);
}
}
decryptResult.Message = "key retrieved successfully";
// return result
return decryptResult;
}
else
{
return new ErrorHandling().handleError("Collection / bucket was not found.");
}
}
这是我迄今为止第一次测试的内容:
public class CouchbaseAPITests
{
private readonly CouchbaseAPI.Controllers.ICouchbaseAPI myClass = Substitute.For<CouchbaseAPI.Controllers.ICouchbaseAPI>();
[Fact]
public async Task document_GET_aKeyIsRetrievedSuccessfully()
{
// Arrange
string bucketName = "myBucket";
string keyName = "myKey";
couchbaseServiceResultClass resultClass = new();
resultClass.Success = true;
resultClass.Message = "key retrieved successfully";
myClass.document_GET(bucketName,keyName).Returns(resultClass);
// Act
var document = await myClass.document_GET(bucketName,keyName);
// Assert
Assert.True(document.Success);
Assert.Equal("key retrieved successfully",document.Message);
}
}
解决方法
如果我们想测试我们是否正确地从 Couchbase API 检索文档,那么通常我们希望尽可能使用该 API 的真实实例(本地测试设置)。如果我们在模拟这个,那么我们的测试并没有真正告诉我们我们的代码是否正常工作(只是我们的模拟正在按照我们希望的方式工作)。
当某些 API 难以使用真实实例时(例如不确定性代码、难以重现网络错误等条件、缓慢的依赖等),那么为该依赖引入接口并模拟我们的测试。
这是一个非常粗略的示例,与发布的代码片段不太匹配,但希望能给您一些有关如何继续操作的想法。
public interface IDataAdapter {
IEnumerable<IGetResult> Get(string key);
}
public class CouchbaseAdapter : IDataAdapter {
/* Implement interface for Couchbase */
}
public class AppApi {
private IDataAdapter data;
public AppApi(IDataAdapter data) {
this.data = data;
}
public SomeResult Lookup(string key) {
try {
var result = data.Get(key);
return Transform(Decrypt(result));
} catch (Exception ex) { /* error handling */ }
}
}
[Fact]
public void TestWhenKeyExists() {
var testAdapter = Substitute.For<IDataAdapter>();
var api = new AppApi(testAdapter);
testAdapter.Get("abc").Returns(/* some valid data */);
var result = api.Lookup("abc");
/* assert that result is decrypted/transformed as expected */
Assert.Equal(expectedResult,result);
}
[Fact]
public void TestWhenKeyDoesNotExist() {
var testAdapter = Substitute.For<IDataAdapter>();
var api = new AppApi(testAdapter);
var emptyData = new List<IGetResult>();
testAdapter.Get("abc").Returns(emptyData);
var result = api.Lookup("abc");
/* assert that result has handled error as expected */
Assert.Equal(expectedError,result);
}
这里我们引入了一个 IDataAdapter
类型,我们的类使用它来抽象我们用来获取数据的实现的细节。我们的真实代码可以使用 CouchbaseAdapter
实现,但我们的测试可以使用模拟版本。对于我们的测试,我们可以模拟数据适配器抛出错误或返回特定信息时发生的情况。
请注意,我们在这里只测试 AppApi
-- 我们没有测试 CouchbaseAdapter
实现,只有 AppApi
会以某种方式响应,如果它的 IDataAdapter
有一定的行为。为了测试我们的 CouchbaseAdapter
,我们需要使用一个真实的实例,但我们不必担心这些细节来测试我们的 AppApi
转换和解密代码。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。