Capturing method arguments on your fakes (using FakeItEasy)

by Marcel Veldhuizen 18. September 2014 21:08

There are many isolation frameworks around that make writing unit tests relatively simple. It's highly unlikely that any single framework will meet all your needs, so writing your own utilities can be useful in order to make your tests easier to write and – perhaps more importantly – read.

I regularly find myself wanting to verify that a certain dependency is called in the right way by my system under test. This includes passed argument values, which can be complex objects themselves.

Suppose I want to verify that my fictitious SUT Circle.CalculateArea calls ICalculator.Square with the radius of the circle. This would be pretty easy with FakeItEasy:

var calculator = A.Fake<ICalculator>(); 
A.CallTo(() => calculator.Square(3)).Returns(9);

var sut = new Circle(3, calculator);
double area = sut.CalculateArea();
Assert.AreEqual(9d, area);

Now imagine that this parameter value is a whole lot more complicated than a double. FakeItEasy provides a syntax for that too. Say I want to verify that the product being saved has the right name:

A.CallTo(
    () => repository.AddProduct(A<Product>.That.Matches(p => p.Name == "MyName")).
    Returns(productId);

Still pretty readable. However, you can imagine if the number of properties to verify grows, this becomes increasingly difficult to read. I prefer to use Assert-methods from my unit testing framework instead. While it's perfectly possible to put these statements into a method and use it when I configure my fake, it's starting to get messy:

A.CallTo(
    () => repository.AddProduct(A<Product>.That.Matches(p =>
    {
        Assert.AreEqual("MyName", p.Name);
        Assert.AreEqual(100m, p.Price);
        // Etcetera
        return true;
    }).
    Returns(productId);

A better solution: capturing the argument value

Rather than using this syntax, you can capture the value passed for the parameter instead. This makes it easier to write a test that follows Arrange-Act-Assert more closely as well. Using some background magic with a helper class it can be pretty easy to read:

// Arrange
const int productId = 42;
var sourceProduct = new Product { Name = "Source Product", Price = 100m }
var productArg = new Capture<Product>();
A.CallTo(() => repository.AddProduct(productArg).Returns(productId);

// Act
sut.DuplicateProduct(sourceProduct);

// Assert
var copy = productArg.Value;
Assert.AreEqual("Copy of Source Product", product.Name);
Assert.AreEqual(sourceProduct.Price, product.Price);

I hope you agree that this scales a lot better than a predicate.

How it works

There's a small amount of magic being done by the Capture<T> class. Take a look at the source code. The amount of code needed for the actual capture is a one-liner, but there is an implicit conversion that enables the terse syntax and then some checks to prevent you from shooting yourself in the foot. There's also a Values property for multiple captures, and HasValues as a shortcut for Values.Count > 0.

Note that the implementation is not thread-safe. Unit tests should avoid dealing with threading if at all possible, but if you need the class to be thread-safe, it is easily added.

How it really really works

For those taking a close look at the implementation: You may be wondering what happens in this case:

var productArg = new Capture<Product>();
A.CallTo(() => repository.SetProductStock(productArg, 0));
A.CallTo(() => repository.SetProductStock(A<Product>._, 1));

repository.SetProductStock(new Product(), 1);

I fully expected the product argument to be captured, even though the constraint on the second parameter is not met. Surprisingly, I could not reproduce this potential problem. FakeItEasy appears to do some deep magic to check the constraints without executing the predicate I'm passing. I've even tried some more complex cases where all constrains were predicates, but all of them seemed to work as desired. Going through the FakeItEasy code I still don't fully understand, so if you can either give me an example which breaks, or explain why it doesn't, I would be most grateful :)

Files:

Tags: ,

Programming | .NET

Comments (32) -

Blair Conrad
9/18/2014 10:01:42 PM #

That's very cool, Marcel.

I've occasionally written one-off capturers, usually inside an Invokes, but this is  a nice example of a general-purpose version of a capturing parameter.

A couple of notes. Your "embedded Assert" syntax is something that I've been people try before. It's a little problematic for reasons other than readability. As soon as multiple rules are added for a method, you have a very real chance that a call will cause one of the Asserts will fail, and that will stop execution before the other "rules" can be checked.

There's no really deep magic about why your product wasn't captured at the end of your example. It's just that fake rules are applied like a stack, so the more recent rule overrides the earlier one. (See "Changing behavior between calls" at github.com/.../Limited-Call-Specifications for another explanation of the "stack" of rules.)

So your

A.CallTo(() => repository.SetProductStock(A<Product>._, 1));

rule becomes the "active" (or at least "first checked") one. When you try to set the product stock with a count of 1, that rule matches, and the one with the capturing productArg is never tried at all.
If you reverse the order of your A.CallTo()s, you'll see the test pass.

Reply

Marcel Veldhuizen
9/18/2014 10:15:30 PM #

Hey Blair,

Yeah, the embedded Assert syntax will abort execution of the rest of the test. That's always the case with Asserts however? Or are you referring to some case where the Assert might be expected not to fail?

The order of the rules makes sense. I thought I tried that as well, but I guess I didn't, or not correctly. I'll try again... thanks Smile

Reply

Blair Conrad
9/18/2014 10:20:12 PM #

Oh, no. Asserts always fail. The difference is that the usual FakeItEasy argument matchers do not cause a catastrophic failure if a non-matching argument comes in. They just don't match, and let another rule have a chance at it. The Assert will _shut you down_.

I'l be keen to hear what you find about the rule ordering. (Spoiler alert: I actually tried it before I commented before; it wasn't just conjecture.)

Reply

tempat tidur anak
4/12/2017 8:54:43 AM #

When you're selecting your camera bag, you might want to consider the backpack style which is large enough to accommodate all camera and accessories and easy to carry, an important feature when you're searching for that great nature shot

Reply

Luis Bise
5/15/2015 7:17:59 PM #

Good and informative. I always visit this site. The actual difference is how the usual FakeItEasy argument matchers don't cause a catastrophic failure if your non-matching argument is available in. Thanks

Reply

balu
7/31/2015 2:27:39 PM #

Thanks for sharing this article. I was impressed about the style of writing.

Reply

Ted
8/18/2015 11:56:53 PM #

Very impressive syntax, thanks for the enlightenment.

Reply

melisa
11/1/2016 1:57:50 PM #

They just don't match, and let another rule have a chance at it.
staminapasutri.com/cream-kuda-hitam-ajaib/.html

Reply

bem estar globo horario United States
11/19/2016 1:05:24 PM #

Reply

viver bem a Vida frases United States
11/19/2016 1:11:41 PM #

Reply

Bem Viver clinica de saude no trabalho United States
11/19/2016 1:19:07 PM #

Estudar sempre algo diferente pode ser um bom jeito de obrigar sua cabeça a pensar mais.

Reply

bem estar animal suinos United States
11/19/2016 1:24:22 PM #

Reply

vida saudavel desenho United States
11/19/2016 1:33:46 PM #

Além disso, você terá atendimento diferenciado do nosso consultor Consul, que acompanhará todo seu processo de entrega e tirará qualquer dúvida por e-mail ou telefone.

Reply

viver bem sempre United States
11/19/2016 1:43:19 PM #

E ainda esse bom bate-papo faz com que nossa vida adquira um significado mesmo que seja momentâneo.

Reply

viver bem moveis United States
11/19/2016 1:47:38 PM #

Grafite que a gente conhece hoje nasceu nas ruas como uma forma de expressão da periferia, mas já faz tempo que ele chegou a museus e galerias e, melhor ainda, passou a fazer parte da decoração, cobrindo portas, paredes e tetos, e tornando banheiros, salas e quartos bem mais interessantes.

Reply

Bem Estar Animal Navegantes United States
11/19/2016 1:52:29 PM #

E resultado dessa exaustão é estresse diário, cada vez mais presente na vida moderna, portanto sendo necessário aprender a lidar com ele.

Reply

momentosadois.my-free.website United States
11/19/2016 2:21:04 PM #

Reply

bem estar animal suinos United States
11/19/2016 3:03:27 PM #

Essa variedade de atores destaca não somente a expansão geográfica e a extensão do impacto de múltiplos atores, mas também a migração de poder de cima para baixo e do centro para a periferia - com quase 7 bilhões de pessoas que querem e têm a capacidade de ser ouvidas.

Reply

dietafit.ucoz.net United States
11/22/2016 6:01:50 PM #

Reply

ทํานายเบอร์โทรศัพท์ United States
11/27/2016 7:14:09 PM #

This paragraph gives clear idea in favor of the new people of blogging, that genuinely how to do blogging and site-building.

Reply

mumfordmarling.tumblr.com United States
12/6/2016 3:59:33 AM #

Geological Survey ecologist is concerned for plant life at the boundaries of glaciers. When you're selecting your camera bag, you might want to consider the backpack style which is large enough to accommodate all camera and accessories and easy to carry, an important feature when you're searching for that great nature shot. Don't be discouraged if they don't all get it right away.  My web page :: mountain ( mumfordmarling.tumblr.com - mumfordmarling.tumblr.com/.../this-is-the-best-place-for-hiking-with-family )

Reply

sanju
12/16/2016 4:03:28 PM #

very nice

Reply

Janio Fagundes
12/22/2016 8:38:20 AM #

I was impressed about the style of writing.

Thanks for sharing this article.

Janio

Reply

kemeja pria
1/6/2017 9:17:04 AM #

Nicee Articel Smile
<a href="http://www.sepatumurahku.com";>Jual Sepatu Murah</a>
<a href="http://www.sepatumurahku.com";>Sepatu Online</a>
<a href="http://www.sepatumurahku.com";>Grosir Sepatu Murah</a>
<a href="http://www.sepatumurahku.com";>Sepatu Murah</a>
<a href="http://http://kemejapedia.com";>kemeja</a>
<a href="http://http://kemejapedia.com";>kemeja pria</a>
<a href="http://http://kemejapedia.com";>kemeja flanel</a>

Reply

jogos da masha e o urso de pintar United States
1/16/2017 5:17:19 PM #

Reply

mobile legends bang bang cheats United States
1/25/2017 12:40:39 PM #

Reply

Awais
2/5/2017 5:34:48 PM #

Very Nice article, Thanks For sharing This Great Info

Reply

in vitro fertilization United States
2/10/2017 11:02:41 AM #

If you got stretchmarks during puberty on the breasts or elsewhere, you could be at greater risk.  You'll additionally invest in an ovulation kit which can be simple to operate and definately will tell you precisely if the correct time to try and conceive is. You should gain bodyweight steadily whilst pregnant as an alternative to placed on lots of bodyweight all in the same  course you may need to take frequent work out but always take advice out of your GP.

Reply

receitas de sucos detox United States
3/3/2017 6:00:09 AM #

Benefícios: Vegetais e raízes ricos em fibras ajudam no funcionamento do intestino e gengibre ajuda na digestão.

Reply

Deana United States
4/11/2017 5:45:36 PM #

Hello colleagues, how is everything, and what you would like to say about this piece of writing, in my view its really amazing in support of me.

Reply

Carmine United States
4/20/2017 6:28:48 AM #

I am actually happy to read this weblog posts which includes tons of valuable facts, thanks for providing these kinds of information.

Reply

Ted
4/20/2017 4:44:08 PM #

Great article thank you for the information.

Reply

Add comment

Marcel Veldhuizen

Software Architect by profession, mostly focused on Microsoft .NET technology.

Metal enthusiast; I love going to concerts :)

 

RecentPosts

Recent Tweets

Note: For Customization and Configuration, CheckOut Recent Tweets Documentation