public class Worker { private List <int> workHours {get; set; } public int GetTotalHoursWorked () {return workHours.Sum (); } } public class Payroll { public int CalculatePay (Worker worker) { // pay everyone 10 dollars an hour (I am evil) return worker.GetTotalHoursWorked () * 10; } }
Payroll
class, but in order for it to be a real unit test, we need to isolate the dependent Person
class so that, for example, the GetTotalHoursWorked()
function is not called at all . How to do it? Very simple: First, we create a Payroll
as usual, but instead of Person
we create a mock object:Worker w = Isolate.Fake.Instance <Worker> (); Payroll p = new Payroll ();
Worker
will not be affected. To do this, you need to change the GetTotalHoursWorked()
call. Here is how it is done:Isolate.WhenCalled (() => w.GetTotalHoursWorked ()). WillReturn (40);
Person.GetTotalHoursWorked()
number 40 will be Person.GetTotalHoursWorked()
returned. If you do not believe it, set a breakpoint on the function and you will see that no one is included in the testing.int result = p. CalculatePay (w);
workHours
not initialize workHours
— the value is null
. However, we have a very realistic result - 400. Moreover, we can even check that the GetTotalHoursWorked()
method was actually called (the fact that the changed method was called does not matter). This is the last phase of AAA, namely Assert. We look:Assert.AreEqual (400, result); Isolate.Verify.WasCalledWithAnyArguments (() => w.GetTotalHoursWorked ());
[Test] public void TestPayroll () { // Arrange Payroll p = new Payroll (); Worker w = Isolate.Fake.Instance <Worker> (); Isolate.WhenCalled (() => w.GetTotalHoursWorked ()). WillReturn (40); // Act int result = p. CalculatePay (w); // Assert Assert.AreEqual (400, result); Isolate.Verify.WasCalledWithAnyArguments (() => w.GetTotalHoursWorked ()); }
Payroll.CalculatePay()
method, replacing the Person
parameter with a certain similarity that behaved predictably and did not affect the actual properties and methods of the class.Worker.GetTotalHoursWorked()
method is in a different assembly, and is marked as internal
:public class Worker { private List <int> workHours {get; set; } internal int GetTotalHoursWorked () {return workHours.Sum (); } }
GetTotalHoursWorked()
no longer have access to it:// will not work Isolate.WhenCalled (() => w.GetTotalHoursWorked ()). WillReturn (40); // this too Isolate.Verify.WasCalledWithAnyArguments (() => w.GetTotalHoursWorked ());
Isolator.NonPublic
we can set the method by name:Isolate.NonPublic.WhenCalled (w, "GetTotalHoursWorked"). WillReturn (40); ... Isolate.Verify.NonPublic.WasCalled (w, "GetTotalHoursWorked");
operator this[]
). Well, checks for calls can be done accordingly.new
operator, TypeMock can also work with static objects. For example, to fake a static constructor, we simply call Isolate.Fake.StaticConstructor(typeof (T));
, and then use TypeMock as before. The same is done with static methods.public class Dog { public Dog () {} public string MakeSound () { return "Woof"; } } public class Duck { public string MakeSound () { return "Quack"; } }
[TestFixture, Isolated] public class Tests { [Test] public void Test () { // fake a dog Dog dog = Isolate.Fake.Instance <Dog> (); Duck duck = new Duck (); // replace calls on dog with calls on duck Isolate.Swap.CallsOn (dog) .WithCallsTo (duck); // get a dog to quack string sound = dog.MakeSound (); // did it? Assert.AreEqual ("Quack", sound); Isolate.Verify.WasCalledWithAnyArguments (() => dog.MakeSound ()); } }
Test
attribute, the Isolated
attribute must be used either at the method level or at the class level — this attribute allows you to clear the context of the used mock objects.Source: https://habr.com/ru/post/56917/
All Articles