using System; using Moq; namespace MoqSample { class Program { static void Main(string[] args) { // ARRANGE decimal amount = 100; // that's why you will love unit testing, mishaps could be avoided :-) string errorMessage = "Could be an anomaly, passed amount is different from amount to transfer"; var mockedSource = new Mock<IAccount>(); mockedSource.Setup(x => x.Withdraw(It.Is<decimal>(a => a != amount))).Throws(new Exception(errorMessage)); mockedSource.Setup(x => x.Deposit(It.Is<decimal>(a => a != amount))).Throws(new Exception(errorMessage)); var mockedDest = new Mock<IAccount>(); mockedDest.Setup(x => x.Withdraw(It.Is<decimal>(a => a != amount))).Throws(new Exception(errorMessage)); mockedDest.Setup(x => x.Deposit(It.Is<decimal>(a => a != amount))).Throws(new Exception(errorMessage)); // ACT int test = 0; /* * 0's error: NONE * * 1's error: * Expected invocation on the mock should never have been performed, but was 1 times: source => source.Deposit(.amount) * * 2's error: * Expected invocation on the mock once, but was 2 times: dest => dest.Deposit(.amount) * * 3's error: * Expected invocation on the mock once, but was 0 times: dest => dest.Deposit(.amount) * * 4's error: * Expected invocation on the mock once, but was 0 times: source => source.Withdraw(.amount) * * 5's error: * Could be an anomaly, passed amount is different from transfer amount * * */ if (test == 0) TransactionMaker.TransferFund(mockedSource.Object, mockedDest.Object, amount); else if (test == 1) TransactionMaker.TransferFundBuggy1(mockedSource.Object, mockedDest.Object, amount); else if (test == 2) TransactionMaker.TransferFundBuggy2(mockedSource.Object, mockedDest.Object, amount); else if (test == 3) TransactionMaker.TransferFundBuggy3(mockedSource.Object, mockedDest.Object, amount); else if (test == 4) TransactionMaker.TransferFundBuggy4(mockedSource.Object, mockedDest.Object, amount); else if (test == 5) TransactionMaker.TransferFundAnomalous(mockedSource.Object, mockedDest.Object, amount); else throw new Exception("Select test case from 0 to 5"); // ASSERT mockedSource.Verify(source => source.Withdraw(amount), Times.Once()); mockedSource.Verify(source => source.Deposit(amount), Times.Never()); mockedDest.Verify(dest => dest.Deposit(amount), Times.Once()); mockedDest.Verify(dest => dest.Withdraw(amount), Times.Never()); } } public static class TransactionMaker { public static void TransferFund(IAccount sourceAccount, IAccount destAccount, decimal amountToTransfer) { sourceAccount.Withdraw(amountToTransfer); destAccount.Deposit(amountToTransfer); } public static void TransferFundBuggy1(IAccount sourceAccount, IAccount destAccount, decimal amountToTransfer) { sourceAccount.Withdraw(amountToTransfer); sourceAccount.Deposit(amountToTransfer); } public static void TransferFundBuggy2(IAccount sourceAccount, IAccount destAccount, decimal amountToTransfer) { sourceAccount.Withdraw(amountToTransfer); destAccount.Deposit(amountToTransfer); destAccount.Deposit(amountToTransfer); } public static void TransferFundBuggy3(IAccount sourceAccount, IAccount destAccount, decimal amountToTransfer) { sourceAccount.Withdraw(amountToTransfer); } public static void TransferFundBuggy4(IAccount sourceAccount, IAccount destAccount, decimal amountToTransfer) { sourceAccount.Deposit(amountToTransfer); } public static void TransferFundAnomalous(IAccount sourceAccount, IAccount destAccount, decimal amountToTransfer) { sourceAccount.Withdraw(amountToTransfer); destAccount.Deposit(amountToTransfer + 0.5M); } } public interface IAccount { void Withdraw(decimal v); void Deposit(decimal v); } public class Account : IAccount { public void Withdraw(decimal amount) { } public void Deposit(decimal amount) { } } }
"Simplicity can't be bought later, it must be earned from the start" -- DB
Showing posts with label Moq. Show all posts
Showing posts with label Moq. Show all posts
Monday, February 27, 2012
A primer on unit testing with Moq
The following code is a primer on unit testing, just get Moq from Nuget. Note: Actual unit testing should be done on a separate project, not on Main method :-) This code just demonstrates how elegant Moq is for unit testing needs.
Monday, January 23, 2012
Liskov Substitution Principle, a simple example
The Liskov Substitution Principle of object oriented design states:
The code above violates Liskov Substitution Principle, there should be no side effects on results even I use Square object for Rectangle purposes. Don't override properties just because it look smart to do so.
To set the Square's sides properly, set the two sides via one property only:
In class hierarchies, it should be possible to treat a specialized object as if it were a base class object.
using System; namespace Liskov { class Rectangle { int _height; public virtual int Height { get{ return _height; } set { _height = value; } } int _width; public virtual int Width { get { return _width; } set { _width = value; } } public int Area { get { return _width * _height; } } public Rectangle (int width, int height) { Height = height; Width = width; } } class Square : Rectangle { public override int Height { set { base.Height = value; base.Width = value; } } public override int Width { set { base.Width = value; base.Height = value; } } public Square (int side) : base(width: side, height: side) { } } class MainClass { public static void Main (string[] args) { Rectangle s = new Square(side: 10); // 100, correct Console.WriteLine ("Area: {0}", s.Area); // reusing the object r as rectangle object s.Height = 4; s.Width = 3; // expecting 12, actual output is 9. incorrect. Liskov Substitution Principle is violated Console.WriteLine ("Area: {0}", s.Area); } } }
The code above violates Liskov Substitution Principle, there should be no side effects on results even I use Square object for Rectangle purposes. Don't override properties just because it look smart to do so.
To set the Square's sides properly, set the two sides via one property only:
public int Side { get { return Height; } set { Height = Width = value; } }
Subscribe to:
Posts (Atom)