C#如何在没有实现的情况下单元测试接口方法
我是单元测试和计算器的新手。C#如何在没有实现的情况下单元测试接口方法
我在下面的接口测试RefreshAmount
:
public interface IAccountService
{
double GetAccountAmount(int accountId);
}
这里是取决于这个接口的类:
public class AccountObj
{
private readonly int _Id;
private readonly IService _service;
public AccountObj(int Id, IService service)
{
_Id = Id;
_service = service;
}
public double Amount { get; private set; }
public void RefreshAmount()
{
Amount = _service.GetAmount(_Id);
}
}
我的单元测试的RefreshAmount
的行为如何?
RefreshAmount
致电IService.GetAmount
可能会打电话给后台办公室,但我没有实施。任何建议的路上将不胜感激。 (我看了一下起订量和依赖注入,但我安静新的单元测试)
使用起订量,这里是有意见
[TestClass]
public class AccountObjUnitTests {
[TestMethod]
public void AccountObj_Given_Id_RefreshAmount_Should_Return_Expected_Amount() {
//Arrange
//creating expected values for test
var expectedId = 1;
var expectedAmount = 100D;
//mock implementation of service using Moq
var serviceMock = new Mock<IService>();
//Setup expected behavior
serviceMock
.Setup(m => m.GetAmount(expectedId))//the expected method called with provided Id
.Returns(expectedAmount)//If called as expected what result to return
.Verifiable();//expected service behavior can be verified
//the system under test
var sut = new AccountObj(expectedId, serviceMock.Object);
//Act
//exercise method under test
sut.RefreshAmount();
//Assert
//verify that expectations have been met
serviceMock.Verify(); //verify that mocked service behaved as expected
Assert.AreEqual(expectedAmount, sut.Amount);
}
//Samples class and interface to explain example
public class AccountObj {
private readonly int _Id;
private readonly IService _service;
public AccountObj(int Id, IService service) {
_Id = Id;
_service = service;
}
public double Amount { get; private set; }
public void RefreshAmount() {
Amount = _service.GetAmount(_Id);
}
}
public interface IService {
double GetAmount(int accountId);
}
}
一个小例子测试这里是相同的测试
[TestMethod]
public void AccountInfo_RefreshAmount_Given_Id_Should_Return_Expected_Amount() {
//Arrange
//creating expected values for test
var expectedId = 1;
var expectedAmount = 100D;
//mock implementation of service using Moq with expected behavior
var serviceMock = Mock.Of<IService>(m => m.GetAmount(expectedId) == expectedAmount);
//the system under test
var sut = new AccountObj(expectedId, serviceMock);
//Act
sut.RefreshAmount();//exercise method under test
//Assert
Assert.AreEqual(expectedAmount, sut.Amount);//verify that expectations have been met
}
我不知道起订量框架。我们使用MSTest和Microsoft Fakes这可以为您提供存根。
然而,天真的直接的方式可以实现在测试类
public class MyTestImplementation : IAccountService
{
public bool HasBeenCalled { get; private set; }
public int ProvidedId { get; private set; }
public double Amoung { get; set; }
public double GetAccountAmount(int accountId)
{
HasBeenCalled = true;
ProvidedId = accountId;
}
}
的接口和测试方法:
[TestMethod] // or whatever attribute your test framework uses
public void TestInterface()
{
const double EXPECTEDAMOUNT = 341;
const int EXPECTEDID = 42;
MyTestImplementation testImpl = new MyTestImplementation();
testImpl.Amount = EXPECTEDAMOUNT;
var sut = new AccountObj(EXPECTEDID, testImpl);
sut.RefreshAmount();
// use your assertion methods here
Assert.IsTrue(testImpl.HasBeenCalled);
Assert.AreEqual(EXPECTEDID, testImpl.ProvidedID);
Assert.AreEqual(EXPECTEDAMOUNT, sut.Amount);
}
如果你只是想检查由此产生的Amount
是正确的,你可以忽略其他属性。通常情况下,你不想检查RefreshAmount()
如何做它应该做的,但只有当产生Amount
是正确的。这种形式的东西
感谢您的快速回答。因此,如果我从您的代码中了解,测试即将检查该方法是否已被有效调用,以及提供的Id是否正确。对?这不是检查返回值是否正确。我在这里得到这个概念吗? –
@AxelBetton哦,对,补充说,验证。我有时候仍然感到困惑 - 我应该真的测试一下。 –
单元测试是关系到你的类的好作文。在这种情况下是。您可以将“服务”传递给您的班级,并为您完成工作。
什么,你需要做的是让“testServiceClass”,将工作只是为了测试,但不会做别的。
注:下面代码是丢失了所有的“测试” atributes向大家介绍的代码
namespace MyTests
{
public class AccountObjTest
{
public void Test1()
{
int testVal = 10;
AccountObj obj = new AccountObj(testVal, new AccountObjTestService());
obj.RefreshAmount();
Assert.AreEquals(obj.Amount, testVal);
}
}
internal class AccountObjTestService : IAccountService
{
public override double GetAmount(int accountId)
{
return accountId;
}
}
}
这是重要的,你的是,类本身被选中(单元测试),而不是整个实施几个类(INTEGRATION-test)。
没错。如果没有注入的实现,则无法测试接口。你需要某种实现,或者模拟接口和方法。 – Sietse
更简化的版本,我强烈建议你使用起订量为你正在努力实现。它将允许你“伪装”你正在传递给你想要测试的类的任何接口的实现。这样你一次只能测试一个类的行为。从长远来看,这将使单元测试变得更加容易。
创建一个测试类似乎是一个简单的选择,但是,如果您要处理大量场景,那么测试类将不得不增长并且变得更复杂以迎合所有场景,并且在某个点由于其复杂性,您也必须对其进行测试。
试着学习如何使用moq,它将在长期内得到回报。您将能够验证传递给模拟对象的数据,控制模拟行为返回的内容,测试是否调用了模拟方法以及调用了多少次。 Moq非常有用。
看一看https://github.com/Moq/moq4/wiki/Quickstart它解释了如何使用起订量得很好begginers
我认为,如果你在你的代码中的一些输入错误,因为你的界面被称为IAccountService
而你的服务实现IService
。你的班级名为AccountObj
,而你的构造函数名为AccountInfo
。
因此,让我们假设您的服务预计实施IAccountService
,并且您的班级预计名为AccountInfo
。
在编写单元测试以测试AccountInfo.RefreshAmount
时,您必须了解此功能的要求;你必须确切知道这个函数应该做什么。
看代码似乎的RefreshAmount
的要求是:
无论实现
IAccountService
和任何标识用于构建AccountInfo
类的对象对象,调用RefreshAmount
的后置条件是财产Amount
返回与IAccountService.GetAccountAmount(Id)
相同的值。
或者更正式的:
- 对于任何标识
- 为应调用RefreshAmount(后实现
IAccountService
- 正在使用Id和IAccountService创建的对象的任何类),返回一个值对于财产金额,等于由IAccountService.GetAccountAmount(Id)返回的值。
因为要求说它应该适用于所有的ID和所有的AccountServices,所以你不能用所有的ID来测试你的课程。在编写单元测试的这种情况下,你必须考虑可能发生的错误。
在你的例子中,它看起来没有可能的错误,但是对于将来的版本,你可能会想到该函数会用不正确的ID调用服务,或者调用不正确的服务,或者忘记保存返回值财产金额或该财产金额不会返回正确的值。
您的类的要求指定它应该适用于每个Id和每个IAcocuntService。因此,您可以*地向要测试的测试对象的构造函数提供任何Id和AccountService,这些可能会检测到四个未来错误中的任何一个。
幸运的是一个简单的AccountInfo类就足够
class AccountService : IAccountService
{
public double GetAccountAmount(int accountId)
{
return 137 * accountId - 472;
}
}
以下单元测试的测试,该量是由提供的实现IAccountService的几个IDS返回
空隙TestAccountObj_RefreshAmount() { 量const int Id = 438; IAccountService accountService = new AccountService(); var testObject = new AccountInfo(Id,accountService);
testObject.RefreshAmount();
double amount = testObject.Amount;
double expectedAmount = accountService.GetAccountAmount(Id);
Assert.AreEqual(expectedAmount, amount);
}
此测试将测试所有四个提到可能的错误。它不会找到的唯一的错误是,如果这个不正确的服务将返回完全相同的奇怪计算的数字,它会调用不正确的服务。这就是为什么我把这种奇怪的计算放在服务中,任何不正确调用的服务都不可能返回相同的错误。特别是如果你用不同的Ids测试使用各种TestObject
测试接口本身感觉没有意义,因为没有要测试的实现。 1.编写一个实现并用单元测试进行测试 2.正如你所提到的,你可以使用Moq来为你的接口提供一个测试实现 –
@ J.Tuc我不认为他想测试这个接口, AccountObj'使用该接口。 –
您的'AccountObj'看起来像一个实体。您应该[不要将依赖关系注入到实体的构造函数中](https://*.com/a/28767898/264697)。 – Steven