使用 NSubstitute 使用内部 SDK 调用模拟类

时间:2021-07-12 17:09:31

标签: xunit nsubstitute asp.net5

第一次尝试使用 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);

        }
}

1 个答案:

答案 0 :(得分:0)

如果我们想测试我们是否正确地从 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 转换和解密代码。