我是否正确编写了单元测试? NUnit + NSubstitute
Am I writing my unit tests correctly? NUnit + NSubstitute
我最近开始学习如何编写单元测试,以及要测试单元的哪一部分的功能以及模拟什么。我正在使用 NSubstitute 作为我的模拟框架。我的示例基本上调用了一个 repo class,然后它对外部服务进行 WEB API web 调用,即:AddCreditCard,然后 returns 结果。我为 AddCreditCard 创建了 2 个单元测试,一个用于成功,一个用于失败。我仍然不能 100% 确定我是否正确地完成了所有这些工作。单元测试通过了,但我不确定我的断言是否在正确的数据上完成...欢迎所有帮助和建议!
public interface IApiResponse<T>
{
HttpStatusCode StatusCode { get; set; }
T Result { get; set; }
string ErrorMessage { get; }
bool HasErrors { get; }
}
public interface ISignedRequest
{
void ConfigureSettings(SignedRequestSettings settings);
IApiResponse Post(string jsonData, Dictionary<string, string> parameters = null,
IOptionalHeaders optionalHeaders = null);
}
public class PaymentRepository
{
private readonly SignedRequestSettings _settings;
private readonly ISignedRequest _signedRequest;
public PaymentRepository(ISignedRequest signedRequest = null)
{
if (signedRequest == null)
_signedRequest = new SignedRequest();
else
_signedRequest = signedRequest;
}
public IApiResponse AddCreditCard(CreditCard request)
{
var jsonData =
JsonConvert.SerializeObject(request);
string action = string.Format("customers/{0}/paymentmethods", request.ConnectId);
_settings.Url = string.Format("{0}{1}", String.Empty, action);
_signedRequest.ConfigureSettings(_settings);
return _signedRequest.Post(jsonData);
}
}
[Test]
public void AddCreditCard_GivenValidCreditCard_ReturnsCreatedResult()
{
//Given
var apiResponse = Substitute.For<IApiResponse>();
apiResponse.StatusCode = HttpStatusCode.Created;
var signedRequest = Substitute.For<ISignedRequest>();
signedRequest.Post(Arg.Any<String>()).Returns(apiResponse);
var creditCard = Substitute.For<CreditCard>();
creditCard.ConnectId = Guid.Parse("1fc1ad83-cd4e-4b68-bce6-e03ee8f47fb6");
var repo = new PaymentRepository(signedRequest);
//When
var addCreditCardResponse = repo.AddCreditCard(creditCard);
//Then
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
Assert.AreEqual(HttpStatusCode.Created, addCreditCardResponse.StatusCode);
}
[Test]
public void AddCreditCard_GivenInvalidCreditCard_ReturnsHasErrorsResult()
{
//Given
var apiResponse = Substitute.For<IApiResponse>();
apiResponse.HasErrors.Returns(true);
apiResponse.ErrorMessage.Returns("Add credit card error message");
var signedRequest = Substitute.For<ISignedRequest>();
signedRequest.Post(Arg.Any<String>()).Returns(apiResponse);
var creditCard = Substitute.For<CreditCard>();
creditCard.ConnectId = Guid.Parse("1fc1ad83-cd4e-4b68-bce6-e03ee8f47fb6");
var repo = new PaymentRepository(signedRequest);
//When
var addCreditCardResponse = repo.AddCreditCard(creditCard);
//Then
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
Assert.AreEqual(apiResponse.HasErrors, addCreditCardResponse.HasErrors);
Assert.AreEqual(apiResponse.ErrorMessage, addCreditCardResponse.ErrorMessage);
}
我认为您的测试基本没问题,但有些地方我会质疑。你的两个测试都在底部有这些行:
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
在您的 signedRequest
替代品上调用这些方法对那些测试真的很重要吗?我建议它可能不是。如果没有进行这些调用,我认为您的测试无论如何都会失败(尽管这在某种程度上是一种风格决定)。
我要说的第二件事是您错过了一项或多项测试(同样,这个数字有点风格化)。就目前而言,您没有验证提供给 ConfigureSettings
或 Post
调用的 signedRequest
替代信息。您的存储库代码可以简单地执行 _signedRequest.Post("some random string");
而您的测试仍然会通过。我会添加另一个测试来验证这些调用,以确保请求确实正确发送。这是 Received
验证有意义的地方。类似于:
signedRequest.Received(1).ConfigureSettings(Arg.Is<SignedRequestSettings>(x=>x.Url == someHardCodedUrl));
signedRequest.Received(1).Post(someHardCodedJsonString);
我最近开始学习如何编写单元测试,以及要测试单元的哪一部分的功能以及模拟什么。我正在使用 NSubstitute 作为我的模拟框架。我的示例基本上调用了一个 repo class,然后它对外部服务进行 WEB API web 调用,即:AddCreditCard,然后 returns 结果。我为 AddCreditCard 创建了 2 个单元测试,一个用于成功,一个用于失败。我仍然不能 100% 确定我是否正确地完成了所有这些工作。单元测试通过了,但我不确定我的断言是否在正确的数据上完成...欢迎所有帮助和建议!
public interface IApiResponse<T>
{
HttpStatusCode StatusCode { get; set; }
T Result { get; set; }
string ErrorMessage { get; }
bool HasErrors { get; }
}
public interface ISignedRequest
{
void ConfigureSettings(SignedRequestSettings settings);
IApiResponse Post(string jsonData, Dictionary<string, string> parameters = null,
IOptionalHeaders optionalHeaders = null);
}
public class PaymentRepository
{
private readonly SignedRequestSettings _settings;
private readonly ISignedRequest _signedRequest;
public PaymentRepository(ISignedRequest signedRequest = null)
{
if (signedRequest == null)
_signedRequest = new SignedRequest();
else
_signedRequest = signedRequest;
}
public IApiResponse AddCreditCard(CreditCard request)
{
var jsonData =
JsonConvert.SerializeObject(request);
string action = string.Format("customers/{0}/paymentmethods", request.ConnectId);
_settings.Url = string.Format("{0}{1}", String.Empty, action);
_signedRequest.ConfigureSettings(_settings);
return _signedRequest.Post(jsonData);
}
}
[Test]
public void AddCreditCard_GivenValidCreditCard_ReturnsCreatedResult()
{
//Given
var apiResponse = Substitute.For<IApiResponse>();
apiResponse.StatusCode = HttpStatusCode.Created;
var signedRequest = Substitute.For<ISignedRequest>();
signedRequest.Post(Arg.Any<String>()).Returns(apiResponse);
var creditCard = Substitute.For<CreditCard>();
creditCard.ConnectId = Guid.Parse("1fc1ad83-cd4e-4b68-bce6-e03ee8f47fb6");
var repo = new PaymentRepository(signedRequest);
//When
var addCreditCardResponse = repo.AddCreditCard(creditCard);
//Then
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
Assert.AreEqual(HttpStatusCode.Created, addCreditCardResponse.StatusCode);
}
[Test]
public void AddCreditCard_GivenInvalidCreditCard_ReturnsHasErrorsResult()
{
//Given
var apiResponse = Substitute.For<IApiResponse>();
apiResponse.HasErrors.Returns(true);
apiResponse.ErrorMessage.Returns("Add credit card error message");
var signedRequest = Substitute.For<ISignedRequest>();
signedRequest.Post(Arg.Any<String>()).Returns(apiResponse);
var creditCard = Substitute.For<CreditCard>();
creditCard.ConnectId = Guid.Parse("1fc1ad83-cd4e-4b68-bce6-e03ee8f47fb6");
var repo = new PaymentRepository(signedRequest);
//When
var addCreditCardResponse = repo.AddCreditCard(creditCard);
//Then
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
Assert.AreEqual(apiResponse.HasErrors, addCreditCardResponse.HasErrors);
Assert.AreEqual(apiResponse.ErrorMessage, addCreditCardResponse.ErrorMessage);
}
我认为您的测试基本没问题,但有些地方我会质疑。你的两个测试都在底部有这些行:
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
在您的 signedRequest
替代品上调用这些方法对那些测试真的很重要吗?我建议它可能不是。如果没有进行这些调用,我认为您的测试无论如何都会失败(尽管这在某种程度上是一种风格决定)。
我要说的第二件事是您错过了一项或多项测试(同样,这个数字有点风格化)。就目前而言,您没有验证提供给 ConfigureSettings
或 Post
调用的 signedRequest
替代信息。您的存储库代码可以简单地执行 _signedRequest.Post("some random string");
而您的测试仍然会通过。我会添加另一个测试来验证这些调用,以确保请求确实正确发送。这是 Received
验证有意义的地方。类似于:
signedRequest.Received(1).ConfigureSettings(Arg.Is<SignedRequestSettings>(x=>x.Url == someHardCodedUrl));
signedRequest.Received(1).Post(someHardCodedJsonString);