Am I going about this the right way? And if so, am I understanding it correctly? I am a little confused.
My project is setup in three different layers. The UI Layer, the Business Layer, and the Data Access Layer. The Business Layer and Data Access Layer are both built off of interfaces.
I am attempting to write Unit Tests using NUnit and Moq.
Here is my example. I want to test GetSum(int x, int y), which is a simple function that just returns x + y. This function exists in CalculatorLogic and implements ICalculatorLogic.
public class CalculatorLogic : ICalculatorLogic
{
public int GetSum(int x, int y)
{
return x + y;
}
}
Now, here is how I am attempting to write a UnitTest.
[TestFixture]
public class CalculatorLogicTests
{
Mock<ICalculatorLogic> calculatorLogicMock;
ICalculatorLogic calculatorLogic;
public CalculatorLogicTests()
{
calculatorLogicMock = new Mock<ICalculatorLogic>();
// now i need to do this setup, right?
calculatorLogicMock.Setup(x => x.GetSum(It.IsAny<int>(), It.IsAny<int>())).Returns(6);
calculatorLogic = calculatorLogicMock.Object;
}
[Test]
public void GetSum_Test()
{
int expectedResult = 3 + 3;
var sum = calculatorLogic.GetSum(3, 3);
Assert.AreEqual(sum, expectedResult);
}
}
Now, the above passes. It runs, and it gets what I am expecting. However, it feels wrong. It's just returning whatever I set it to return in the Setup() call. If I had put a 3 in the Returns() instead of the 6, it would fail.
I must be understanding something wrong. Otherwise, am I really testing my function if I'm telling it what to return?
Okay so you've got an interface ICalculatorLogic
and an implementation of said interface CalculatorLogic
that you wish to write tests for.
You're using a mocking framework Moq
to mock dependencies that your CalculatorLogic
does not have. You should write something like this:
[TestFixture]
public class CalculatorLogicTests
{
// Our unit under test.
ICalculatorLogic calculatorLogic;
[SetUp]
public void SetUp()
{
// This method is called for every [Test] in this class.
// So let's recreate our CalculatorLogic here so that each
// test has a fresh instance.
calculatorLogic = new CalculatorLogic();
}
[Test]
public void GetSum_WithTwoIntegers_ReturnsTheirSum()
{
// Arrange
int expectedResult = 3 + 3;
// Act
var sum = calculatorLogic.GetSum(3, 3);
// Assert
Assert.AreEqual(sum, expectedResult);
}
}
Now let's say you want your GetSum
to log the parameters that it is called with. You might create a logger interface like so:
public interface ILogger {
void Log(int x, int y);
}
You then put it as a dependency by requiring it in the constructor of your CalculatorLogic
class:
public class CalculatorLogic : ICalculatorLogic
{
private readonly ILogger logger;
// Now we have a dependency on ILogger!
public CalculatorLogic(ILogger l) {
logger = l;
}
public int GetSum(int x, int y)
{
// Let's log those numbers!
logger.Log(x, y);
return x + y;
}
}
Then you can write your tests like so (using Moq
):
[TestFixture]
public class CalculatorLogicTests
{
// This guy we want to test.
ICalculatorLogic calculatorLogic;
// Our mock!
Mock<ILogger> loggerMock;
[SetUp]
public void SetUp()
{
// Create the logger mock!
loggerMock = new Mock<ILogger>();
// Inject the logger into our CalculatorLogic!
calculatorLogic = new CalculatorLogic(loggerMock.Object);
}
[Test]
public void GetSum_WithTwoIntegers_ShouldCallLogger()
{
// Arrange
int expectedResult = 3 + 3;
// Act
var sum = calculatorLogic.GetSum(3, 3);
// Assert
Assert.AreEqual(sum, expectedResult);
// Verify that the logger's Log method was called once with x = 3 and y = 3.
loggerMock.Verify(logger => logger.Log(It.Is<int>(x => x == 3), It.Is<int>(y => y == 3)), Times.Once());
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.