AutoFakeItEasy and FakeObject #251

Closed
Dashue opened this Issue Feb 27, 2014 · 18 comments

Comments

3 participants
@Dashue

Dashue commented Feb 27, 2014

Hey!
Thanks for AutoFixture, been doing it manually wrong for years : )
Thanks for AutoFakeItEasy because it's sweet : )

When using AutoData and AutoFakeItEasy I really don't like the way an object of type Fake gets created exposing FakedObject.
I think this was how FIE used to do it.

I would like to ask if this FakedObject is a necessity, been browsing through the source a bit just now.
I would like to make a contribution to remove that FakedObject if possible and welcome. (It reminds me a bit to much about moq's .Object) : )

FIE exposes two different ways of creating a fake:

Fake<IInterface> fake = new Fake<IInterface>();
IInterface fake = A.Fake<IInterface>();

What are your thoughts?

@moodmosaic

This comment has been minimized.

Show comment
Hide comment
@moodmosaic

moodmosaic Feb 27, 2014

Member

The A<T> syntax exposes the mock object, not its Fake<T> wrapper, and Freezing wouldn't work.


AutoFakeItEasy uses internally the Fake<T> type that wraps and exposes the mock object:

var fake = new Fake<T> // This instance can now be frozen.

That's necessary in order to support the concept of Freezing and allow tests to be written like:

[Theory, TestConventions]
public void DoSomethingForwardsCorrectCall(
    [Frozen]Fake<IInterface> mock, 
    Controller sut, 
    string s)
{
    sut.DoSomething(s);
    mock.CallsTo(x => x.MakeIt(s)).MustHaveHappened();
}
Member

moodmosaic commented Feb 27, 2014

The A<T> syntax exposes the mock object, not its Fake<T> wrapper, and Freezing wouldn't work.


AutoFakeItEasy uses internally the Fake<T> type that wraps and exposes the mock object:

var fake = new Fake<T> // This instance can now be frozen.

That's necessary in order to support the concept of Freezing and allow tests to be written like:

[Theory, TestConventions]
public void DoSomethingForwardsCorrectCall(
    [Frozen]Fake<IInterface> mock, 
    Controller sut, 
    string s)
{
    sut.DoSomething(s);
    mock.CallsTo(x => x.MakeIt(s)).MustHaveHappened();
}
@Dashue

This comment has been minimized.

Show comment
Hide comment
@Dashue

Dashue Feb 27, 2014

Hey thanks for your time!

I was assuming my naive approach would be missing something like that : )
I'll try to read up on what Freezing does, to better get the picture.

Dashue commented Feb 27, 2014

Hey thanks for your time!

I was assuming my naive approach would be missing something like that : )
I'll try to read up on what Freezing does, to better get the picture.

@ploeh

This comment has been minimized.

Show comment
Hide comment
@ploeh

ploeh Feb 27, 2014

Member

As @moodmosaic explains, this is by design, because it enables you to request a Fake<T> instead of just T.

If you don't like that, you can just request T itself:

var result = fixture.Create<IInterface>();

So, you're not forced to use Fake<T>, but you can if you will.

Member

ploeh commented Feb 27, 2014

As @moodmosaic explains, this is by design, because it enables you to request a Fake<T> instead of just T.

If you don't like that, you can just request T itself:

var result = fixture.Create<IInterface>();

So, you're not forced to use Fake<T>, but you can if you will.

@ploeh ploeh added the question label Feb 27, 2014

@Dashue

This comment has been minimized.

Show comment
Hide comment
@Dashue

Dashue Feb 27, 2014

Hey and thanks for giving input on this! Changing it from Fake<T> to T makes me unable to assert calls on it. I realize I should have been more descriptive in my initial post and I apologize.

using FakeItEasy wether using any of the following

Fake<IInterface> fake = new Fake<IInterface>();
IInterface fake = A.Fake<IInterface>();

Both allow for setup and verification on the fake variable

A.CallTo(() => fake.FakedObject.Call()).etc
A.CallTo(() => fake.Call()).etc

To briefly summarize my intent: I wanted to investigate if it would be possible to leverage FIE in such a way as to have AutoData create and inject Fakes more in the "FIE" way without the FakedObject property.

That in my opinion would make it an even better product, my current impression is I'm cleaning up some code only to pollute with .FakedObject.

Pasting some code to show what i mean to prevent any missunderstandings

        [Theory, AutoData]
        public void Doesnt_work(IIdentity identify)
        {
            A.CallTo(() => identify.AuthenticationType).Returns("string");
        }

        [Theory, AutoData]
        public void Works(Fake<IIdentity> identify)
        {
            A.CallTo(() => identify.FakedObject.AuthenticationType).Returns("string");
        }

        [Theory, AutoData]
        public void Also_Works()
        {
            IIdentity identify = A.Fake<IIdentity>();
            A.CallTo(() => identify.AuthenticationType).Returns("string");
        }

Thanks for sticking with me, I appreciate the work you're doing. I was doing manual overrides for each constructor with optional parameters before, so huge fan of autofixture now : )

Dashue commented Feb 27, 2014

Hey and thanks for giving input on this! Changing it from Fake<T> to T makes me unable to assert calls on it. I realize I should have been more descriptive in my initial post and I apologize.

using FakeItEasy wether using any of the following

Fake<IInterface> fake = new Fake<IInterface>();
IInterface fake = A.Fake<IInterface>();

Both allow for setup and verification on the fake variable

A.CallTo(() => fake.FakedObject.Call()).etc
A.CallTo(() => fake.Call()).etc

To briefly summarize my intent: I wanted to investigate if it would be possible to leverage FIE in such a way as to have AutoData create and inject Fakes more in the "FIE" way without the FakedObject property.

That in my opinion would make it an even better product, my current impression is I'm cleaning up some code only to pollute with .FakedObject.

Pasting some code to show what i mean to prevent any missunderstandings

        [Theory, AutoData]
        public void Doesnt_work(IIdentity identify)
        {
            A.CallTo(() => identify.AuthenticationType).Returns("string");
        }

        [Theory, AutoData]
        public void Works(Fake<IIdentity> identify)
        {
            A.CallTo(() => identify.FakedObject.AuthenticationType).Returns("string");
        }

        [Theory, AutoData]
        public void Also_Works()
        {
            IIdentity identify = A.Fake<IIdentity>();
            A.CallTo(() => identify.AuthenticationType).Returns("string");
        }

Thanks for sticking with me, I appreciate the work you're doing. I was doing manual overrides for each constructor with optional parameters before, so huge fan of autofixture now : )

@ploeh

This comment has been minimized.

Show comment
Hide comment
@ploeh

ploeh Feb 27, 2014

Member

The reason the first test doesn't work is because AutoFakeItEasyCustomization isn't being applied. This, however, works:

[Fact]
public void FixtureReturnsInterfaceWhichCanBeConfigured()
{
    var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization());
    var td = fixture.Create<IIdentity>();
    A.CallTo(() => td.AuthenticationType).Returns("string");

    var actual = td.AuthenticationType;

    Assert.Equal("string", actual);
}

The [AutoData] attribute belongs to AutoFixture.Xunit and knows nothing about any dynamic Mock library, neither FakeItEasy nor any other Mock library.

Here's how to combine AutoFixture.Xunit with AutoFixture.AutoFakeItEasy.

Member

ploeh commented Feb 27, 2014

The reason the first test doesn't work is because AutoFakeItEasyCustomization isn't being applied. This, however, works:

[Fact]
public void FixtureReturnsInterfaceWhichCanBeConfigured()
{
    var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization());
    var td = fixture.Create<IIdentity>();
    A.CallTo(() => td.AuthenticationType).Returns("string");

    var actual = td.AuthenticationType;

    Assert.Equal("string", actual);
}

The [AutoData] attribute belongs to AutoFixture.Xunit and knows nothing about any dynamic Mock library, neither FakeItEasy nor any other Mock library.

Here's how to combine AutoFixture.Xunit with AutoFixture.AutoFakeItEasy.

@Dashue

This comment has been minimized.

Show comment
Hide comment
@Dashue

Dashue Feb 27, 2014

Ah thanks for the further explanation. Then it seems what I would like to have is not achievable. Thank you

Dashue commented Feb 27, 2014

Ah thanks for the further explanation. Then it seems what I would like to have is not achievable. Thank you

@ploeh

This comment has been minimized.

Show comment
Hide comment
@ploeh

ploeh Feb 27, 2014

Member

Why is that? What are you trying to achieve, then?

Member

ploeh commented Feb 27, 2014

Why is that? What are you trying to achieve, then?

@Dashue

This comment has been minimized.

Show comment
Hide comment
@Dashue

Dashue Feb 27, 2014

I wanted to have the Doesnt_work scenario to have a FIE fake injected but not of the Fake type, so as to be able to do setup and assertions on the injected type without going through FakedObject.

I'll settle for creating fakes I setup and assert against manually in the arrange. Actually gives a nice convention showing what's being setupped and acted upon

Dashue commented Feb 27, 2014

I wanted to have the Doesnt_work scenario to have a FIE fake injected but not of the Fake type, so as to be able to do setup and assertions on the injected type without going through FakedObject.

I'll settle for creating fakes I setup and assert against manually in the arrange. Actually gives a nice convention showing what's being setupped and acted upon

@moodmosaic

This comment has been minimized.

Show comment
Hide comment
@moodmosaic

moodmosaic Feb 27, 2014

Member

Well, actually the FakedObject instance is transparent to you:

[Theory, TestConventions]
public void DoSomethingForwardsCorrectCall(
    [Frozen]Fake<IInterface> mock, 
    Controller sut, 
    string s)
{
    sut.DoSomething(s);
    mock.CallsTo(x => x.MakeIt(s)).MustHaveHappened();
}

The problem with A<T> syntax is that even if you decorate it with [Frozen] you won't be able to verify expectations on the mocked object(s) because all it gives you is the mock object itself.

Member

moodmosaic commented Feb 27, 2014

Well, actually the FakedObject instance is transparent to you:

[Theory, TestConventions]
public void DoSomethingForwardsCorrectCall(
    [Frozen]Fake<IInterface> mock, 
    Controller sut, 
    string s)
{
    sut.DoSomething(s);
    mock.CallsTo(x => x.MakeIt(s)).MustHaveHappened();
}

The problem with A<T> syntax is that even if you decorate it with [Frozen] you won't be able to verify expectations on the mocked object(s) because all it gives you is the mock object itself.

@Dashue

This comment has been minimized.

Show comment
Hide comment
@Dashue

Dashue Feb 27, 2014

Aha so it's actually a problem with A.CallTo, will take the CallsTo syntax for a spin. Thanks for the tip!

Dashue commented Feb 27, 2014

Aha so it's actually a problem with A.CallTo, will take the CallsTo syntax for a spin. Thanks for the tip!

@moodmosaic

This comment has been minimized.

Show comment
Hide comment
@moodmosaic

moodmosaic Feb 27, 2014

Member

I don't think there is a problem with the A<T> syntax other than being a different API...

Member

moodmosaic commented Feb 27, 2014

I don't think there is a problem with the A<T> syntax other than being a different API...

@ploeh

This comment has been minimized.

Show comment
Hide comment
@ploeh

ploeh Feb 27, 2014

Member

Oh, but this works too:

[Theory, AutoFakeItEasyData]
public void SetupOnInjectedInterface(
    [Frozen]IIdentity identity,
    Controller sut,
    string expected)
{
    A.CallTo(() => identity.AuthenticationType).Returns(expected);
    var actual = sut.GetAuthNType();
    Assert.Equal(expected, actual);
}

Auxiliary class:

public class AutoFakeItEasyDataAttribute : AutoDataAttribute
{
    public AutoFakeItEasyDataAttribute()
        : base(new Fixture().Customize(new AutoFakeItEasyCustomization()))
    {
    }
}

And for completeness sake, here's the SUT:

public class Controller
{
    private readonly IIdentity identity;

    public Controller(IIdentity identity)
    {
        this.identity = identity;
    }

    public string GetAuthNType()
    {
        return this.identity.AuthenticationType;
    }
}
Member

ploeh commented Feb 27, 2014

Oh, but this works too:

[Theory, AutoFakeItEasyData]
public void SetupOnInjectedInterface(
    [Frozen]IIdentity identity,
    Controller sut,
    string expected)
{
    A.CallTo(() => identity.AuthenticationType).Returns(expected);
    var actual = sut.GetAuthNType();
    Assert.Equal(expected, actual);
}

Auxiliary class:

public class AutoFakeItEasyDataAttribute : AutoDataAttribute
{
    public AutoFakeItEasyDataAttribute()
        : base(new Fixture().Customize(new AutoFakeItEasyCustomization()))
    {
    }
}

And for completeness sake, here's the SUT:

public class Controller
{
    private readonly IIdentity identity;

    public Controller(IIdentity identity)
    {
        this.identity = identity;
    }

    public string GetAuthNType()
    {
        return this.identity.AuthenticationType;
    }
}
@moodmosaic

This comment has been minimized.

Show comment
Hide comment
@moodmosaic

moodmosaic Feb 27, 2014

Member

Yes :) That's similar to Moq's Mock.Get<T> function.

But notice that you've actually frozen an IIdentity not a A<IIdentity> (if it's even possible to use this syntax with an attribute).

Member

moodmosaic commented Feb 27, 2014

Yes :) That's similar to Moq's Mock.Get<T> function.

But notice that you've actually frozen an IIdentity not a A<IIdentity> (if it's even possible to use this syntax with an attribute).

@ploeh

This comment has been minimized.

Show comment
Hide comment
@ploeh

ploeh Feb 27, 2014

Member

Yes, I did that on purpose, because that is what I understood @Dashue wanted.

Member

ploeh commented Feb 27, 2014

Yes, I did that on purpose, because that is what I understood @Dashue wanted.

@moodmosaic

This comment has been minimized.

Show comment
Hide comment
@moodmosaic

moodmosaic Feb 27, 2014

Member

Yes, indeed.

Member

moodmosaic commented Feb 27, 2014

Yes, indeed.

@Dashue

This comment has been minimized.

Show comment
Hide comment
@Dashue

Dashue Feb 27, 2014

@ploeh your inventing all kinds of sweet magic on the fly here. Pure awesomeness. That was definitely what I was after! AutoFakeItEasyDataAttribute is my new hero, I don't even have to freeze it. This falls more in line with FIE syntax imho. I'll happily copy it in all my solutions, but would there be a spot for it in AutoFakeItEasy?

Dashue commented Feb 27, 2014

@ploeh your inventing all kinds of sweet magic on the fly here. Pure awesomeness. That was definitely what I was after! AutoFakeItEasyDataAttribute is my new hero, I don't even have to freeze it. This falls more in line with FIE syntax imho. I'll happily copy it in all my solutions, but would there be a spot for it in AutoFakeItEasy?

@ploeh

This comment has been minimized.

Show comment
Hide comment
@ploeh

ploeh Feb 27, 2014

Member

That attribute was already in the link I previously provided.

It depends both on AutoFixture.Xunit and AutoFixture.AutoFakeItEasy, so it can go in neither of these libraries (because they don't depend on each other). It would be possible to create a new library with such an attribute, but it would (IMO) be overkill with a library that only contains a single attribute. Besides, you'll most likely need to define your own Domain Customization anyway, and as soon as you do that, such a Glue Library would be useless.

Member

ploeh commented Feb 27, 2014

That attribute was already in the link I previously provided.

It depends both on AutoFixture.Xunit and AutoFixture.AutoFakeItEasy, so it can go in neither of these libraries (because they don't depend on each other). It would be possible to create a new library with such an attribute, but it would (IMO) be overkill with a library that only contains a single attribute. Besides, you'll most likely need to define your own Domain Customization anyway, and as soon as you do that, such a Glue Library would be useless.

@ploeh

This comment has been minimized.

Show comment
Hide comment
@ploeh

ploeh Mar 9, 2014

Member

Are there any outstanding concerns regarding this issue, or can we close it?

Member

ploeh commented Mar 9, 2014

Are there any outstanding concerns regarding this issue, or can we close it?

@Dashue Dashue closed this Mar 9, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment