I am new to unit testing and to stackoverflow.
I have to test RefreshAmount
in the following interface:
public interface IAccountService
{
double GetAccountAmount(int accountId);
}
And here is a class that depends on this interface:
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);
}
}
How can I unit test the behavior of RefreshAmount
?
RefreshAmount
calls IService.GetAmount
which may make a call to a back-end office but I do not have its implementation. Any suggestion on the way to go would be appreciated.
(I have read about moq and dependency injection, but I am quiet new to unit testing)
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.
In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.
Because a and b and c , so it's name is C. C came out of Ken Thompson's Unix project at AT&T. He originally wrote Unix in assembly language. He wrote a language in assembly called B that ran on Unix, and was a subset of an existing language called BCPL.
Using Moq, here is a minimal example test with comments
[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);
}
}
And here is a more simplified version of the same test
[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
}
I assume if you have some typing errors in your code, because your interface is called IAccountService
while your service implements IService
. Your class is called AccountObj
while your constructor is called AccountInfo
.
So let's assume your service is expected to implement IAccountService
and your class is expected to be named AccountInfo
.
When writing a unit test to test AccountInfo.RefreshAmount
, you'll have to be aware of the requirements of this function; you'll have to know exactly what this function is supposed to do.
Looking at the code it seems that the requirements of RefreshAmount
is:
Whatever object that implements
IAccountService
and whatever Id are used to construct an object of classAccountInfo
, the post condition of callingRefreshAmount
is that propertyAmount
returns the same value asIAccountService.GetAccountAmount(Id)
would have returned.
Or more formal:
IAccountService
Because the requirement says it should work for all Ids and all AccountServices, you can't test your class with all of them. In such cases when writing unit tests you'll have to think of errors that are likely to happen.
In your example it looks that there are no likely errors, but for future versions you could think of that the function would call the service with the incorrect Id, or call the incorrect service, or forgets to save the return value in property Amount, or that property Amount does not return the proper value.
The requirements for your class specify that it should work for every Id and every IAcocuntService. Therefor you are free to provide any Id and AccountService to the constructor of your test object you want to test, that are likely to detect any of the four future errors.
Luckily a Simple AccountInfo class would Suffice
class AccountService : IAccountService
{
public double GetAccountAmount(int accountId)
{
return 137 * accountId - 472;
}
}
The following unit test tests that the Amount is the amount returned by the provided implementation of IAccountService for several Ids
void 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);
}
This test will test all four mentioned likely errors. The only error that it wouldn't find is that it would call the incorrect service if this incorrect service would return exactly the same strange calculated number. That's why I put such a strange calculation in the service, it is very unlikely that any incorrectly called service would return the same error. Especially if you'd test using various TestObject with various Ids
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With