I am trying to create a simple setup for xUnit. At the moment, the function does not access to database.
So, here is my structure: I have 2 projects in my solution. First is my class library which is going to be the function to be used by the callee. The function is plus and minus 2 numbers for simplicity reason.
namespace XUnitSample.ClassLib.Interfaces
{
public interface ICal
{
int Add(int num1, int num2);
int Minus(int num1, int num2);
}
}
namespace XUnitSample.ClassLib
{
public class Cal : ICal
{
public int Add(int num1, int num2) => num1 + num2;
public int Minus(int num1, int num2) => num1 - num2;
}
}
and the second project is the xUnit test project
namespace XUnitSample.Lib
{
public class CalTest
{
[Fact]
public void Add_Add2Numbers()
{
var mock = new Mock<ICal>();
mock.Setup(x => x.Add(1, 1)).Returns(2);
}
[Theory]
[InlineData(1,2,3)]
[InlineData(1, 1, 2)]
[InlineData(1, 0, 1)]
public void Add_Add2Numbers_Theory(int num1, int num2, int expected)
{
var result = new Cal().Add(num1, num2);
Assert.Equal(expected, result);
}
[Fact]
public void Minus_Minus2Numbers()
{
var mock = new Mock<ICal>();
mock.Setup(x => x.Minus(1, 1)).Returns(0);
}
}
}
All 3 unit test can be run successfully. Any comments on this structure and good practices?
-
2I'm not an xUnit expert, but your first and third tests don't actually seem to be tests, in that they never assert anything. More generally, you should never be mocking the unit you're testing, because then you're not testing your code.Philip Kendall– Philip Kendall2020年06月14日 07:59:20 +00:00Commented Jun 14, 2020 at 7:59
-
You’re testing class Cal. Cal has no dependencies on other classes. Therefor you don’t need to mock anything to test class Cal.Rik D– Rik D2020年06月14日 09:55:48 +00:00Commented Jun 14, 2020 at 9:55
1 Answer 1
When I write a unit test, I use the 'AAA' pattern: Arrange, Act, Assert. For your second unit test, it would look like this:
[Theory]
[InlineData(1,2,3)]
[InlineData(1, 1, 2)]
[InlineData(1, 0, 1)]
public void Add_Add2Numbers_Theory(int num1, int num2, int expected)
{
// Arrange
var cal = new Cal();
// Act
var result = cal.Add(num1, num2);
// Assert
Assert.Equal(expected, result);
}
That way, you have a clear separation of the setup, the execution and the checks in the unit test. More on this subject can be found on numerous sites, like this one: Understand AAA in Unit Testing.
In the first and third unit test, you are using 'mocking' wrong. Mocking is used to replace dependencies. In this case, the ICal
interface is not a dependency of the Cal
class, it is just the interface that the Cal
class is implementing. So there is no use in mocking it.
Mocking comes in place when your Cal
class depends on some other class or library, for example a "CalculationEngine". Then the constructor of the Cal
class looks like this:
public Cal(ICalculationEngine calculationEngine)
{
_calculationEngine = calculationEngine;
}
In such case, when you test the Cal
class, you need to mock the CalculationEngine. And then, you can control the behaviour of this dependency by supplying specific setups for its methods.
An example:
namespace XUnitSample.ClassLib.Interfaces
{
public interface ICalculationEngine
{
int Add(int num1, int num2);
int Minus(int num1, int num2);
}
}
namespace XUnitSample.ClassLib
{
public class Cal
{
private readonly ICalculationEngine _calculationEngine;
public Cal(ICalculationEngine calculationEngine)
{
_calculationEngine = calculationEngine;
}
public int Add(int num1, int num2)
{
return _calculationEngine.Add(num1, num2);
}
public int Minus(int num1, int num2)
{
return _calculationEngine.Minus(num1, num2);
}
}
}
namespace XUnitSample.Lib
{
public class CalTest
{
[Fact]
public void Add_Add2Numbers()
{
// Arrange
var mock = new Mock<ICalculationEngine>();
mock.Setup(x => x.Add(1, 1)).Returns(2);
var cal = new Cal(mock.Object);
// Act
var result = cal.Add(1,1);
// Assert
Assert.Equal(result, 2)
}
}
}
-
Thanks. So why
Cal
should useICalculationEngine
as constructor and not through implementation (Cal : ICalculationEngine
)?Steve– Steve2020年06月14日 09:21:37 +00:00Commented Jun 14, 2020 at 9:21 -
1It was just an example. In real applications, the dependency might be an WebAPI, or a database. These are difficult to use in a unit test, so mocking them makes sense. In this case, I only wanted to show you the difference between implementing an interface, and having a dependency on another class.Frits– Frits2020年06月14日 09:24:41 +00:00Commented Jun 14, 2020 at 9:24
-
For
Add_Add2Numbers_Theory
method, I shall do the same like other methods, right?// Arrange var mock = new Mock<ICal>(); mock.Setup(x => x.Add(num1, num2)).Returns(expected); var cal = new Cal(mock.Object);
Steve– Steve2020年06月14日 09:29:20 +00:00Commented Jun 14, 2020 at 9:29 -
1Yes, you can do that, but be aware that this is not a real-world scenario.Frits– Frits2020年06月14日 16:57:37 +00:00Commented Jun 14, 2020 at 16:57