10

I'm trying to write a unit test of a repository implementation. The repository uses RavenDB as a database. For the unit tests, I would like to mock the RavenDB parts. In order to create the mocks (fakes) I'm using FakeItEasy. I figured there wouldn't be any problems with the mocking/faking since the RavenDB API is accessed through interfaces.

I do however have a problem when trying to instantiate a specific mock. The relevant parts of my unit test code looks like this:

[Fact]
public void Test() {
    UserDocument doc = ...;
    IQueryable<UserDocument> where = A.Fake<IQueryable<UserDocument>>();
    A.CallTo(() => where.First()).Returns(doc);
    IRavenQueryable<UserDocument> query = A.Fake<IRavenQueryable<UserDocument>>();
    IDocumentSession session = A.Fake<IDocumentSession>();
    A.CallTo(() => session.Query<UserDocument>()).Returns(query);
    IDocumentStore store = A.Fake<IDocumentStore>();
    A.CallTo(() => store.OpenSession()).Returns(session);
    .
    .
    .
}

When instantiating the IRavenQueryable fake I get an exception. This is the log from the Xunit.net runner:

UnitTest.Test : FakeItEasy.Core.FakeCreationException : 
  Failed to create fake of type "System.Linq.IQueryable`1[UserDocument]".

  Below is a list of reasons for failure per attempted constructor:
    No constructor arguments failed:
      No default constructor was found on the type System.Linq.IQueryable`1[UserDocument].


Stack Trace:
   vid FakeItEasy.Core.DefaultExceptionThrower.ThrowFailedToGenerateProxyWithResolvedConstructors(Type typeOfFake, String reasonForFailureOfUnspecifiedConstructor, IEnumerable`1 resolvedConstructors)
   vid FakeItEasy.Creation.FakeObjectCreator.TryCreateFakeWithDummyArgumentsForConstructor(Type typeOfFake, FakeOptions fakeOptions, IDummyValueCreationSession session, String failReasonForDefaultConstructor, Boolean throwOnFailure)
   vid FakeItEasy.Creation.FakeObjectCreator.CreateFake(Type typeOfFake, FakeOptions fakeOptions, IDummyValueCreationSession session, Boolean throwOnFailure)
   vid FakeItEasy.Creation.DefaultFakeAndDummyManager.CreateFake(Type typeOfFake, FakeOptions options)
   vid FakeItEasy.Creation.DefaultFakeCreatorFacade.CreateFake[T](Action`1 options)

The "no default constructor found" doesn't make any sense since what I'm trying to fake is an interface. Does anyone have a suggestion what the problem might be?

David Nordvall
  • 12,404
  • 6
  • 32
  • 52
  • I just noticed that your exception message mentions **"System.Linq.IQueryable\`1[UserDocument]"**, not specifically `IRavenQueryable`. Are you sure the exception isn't occurring when you instantiate the `IQueryable`? – Rob Sep 26 '11 at 19:06
  • Yes, I'm sure. However, IRavenQueryable extends the IQueryable interface so I guess FakeItEasy fails when trying to fake the IQueryable part if IRavenQueryable... or something like that. – David Nordvall Sep 27 '11 at 07:12
  • Is the UserDocument-type public? – Patrik Hägne Sep 27 '11 at 08:22
  • 1
    You're correct in that the exception message does not make any sense, this is a bug. It would be great if you could supply a VS-solution that reproduces the bug and file an issue here: https://github.com/patrik-hagne/FakeItEasy/issues?sort=created&direction=desc&state=closed – Patrik Hägne Sep 27 '11 at 08:52
  • For people coming to this question late, note that there have been improvements made in FakeItEasy's "can't fake" error messages, starting in 1.14.0. They aren't perfect, but should be better. See https://github.com/FakeItEasy/FakeItEasy/issues/157 for details, if you've a mind to. – Blair Conrad Mar 15 '14 at 12:18

4 Answers4

11

You're correct in that the exception message does not make any sense, this is a bug. It would be great if you could supply a VS-solution that reproduces the bug and file an issue here: https://github.com/patrik-hagne/FakeItEasy/

The bug is in that the wrong exception message is used, however there must be something wrong that makes the fake creation go wrong. Is the "UserDocument"-type public? If it is internal and you have given your test-project access to it through the use of InternalsVisibleToAttribute you must give the proxy generating library access to it as well: https://fakeiteasy.readthedocs.io/en/stable/how-to-fake-internal-types/#how-to-fake-internal-friend-in-vb-types.

MichaelM
  • 964
  • 7
  • 20
Patrik Hägne
  • 16,751
  • 5
  • 52
  • 60
  • doc topic moved from wiki to separate site: https://fakeiteasy.readthedocs.io/en/stable/how-to-fake-internal-types/ – Vegar Aug 20 '18 at 13:49
3

I know it is an old post, but I ran into the same issue. What I found to be the problem was a return type of one of the methods declared into the interface I was trying to fake. This method was returning an object of a certain class, and this class didn't have a default constructor with which FakeItEasy could work. If someone else gets this error try to check the objects your interface is returning, and if the corresponding classes have the default constructors.

IonutC
  • 607
  • 1
  • 6
  • 11
  • How would you resolve this? Is it possible to fake the method that returns an object that doesn't have a default constructor? – Matt_Bro Aug 06 '13 at 12:49
2

Does the IRavenQueryable<T> interface have a where T : new() type constraint?

If so, and UserDocument does not provide a parameter-less ctor, this might be causing your problem.

Rob
  • 5,223
  • 5
  • 41
  • 62
  • As far as I can tell, there is no such constraint on IRavenQueryable. Even if there is, UserDocument has a parameterless constructor. – David Nordvall Sep 26 '11 at 18:25
2

I just ran into this, but my issue wasn't around internal types. My issue was with the assembly containing the type not being in the bin folder of the unit test project.

It seems like FakeItEasy throws this error when it can't resolve a type that it needs to fake. (This makes sense why an internal type in another assembly would cause the same error.)

So, I had Project Foo, which is referenced by Project Bar. Project Bar had a public interface referencing a public type from Project Foo. Project Bar.Tests has a reference to Project Bar, but not Project Foo. When I build Bar.Tests, Bar.dll gets put in the bin folder but Foo.dll does not. When FakeItEasy tries to fake out my interface, it can't resolve the type which resides in Foo.dll.

Adding a reference to Project Foo in my Bar.Tests project ensured that Foo.dll makes its way over and is there for FakeItEasy and made this error disappear.

So...

In your case, it could be that your RavenDB assembly (which I assume contains UserDocument) is only referenced by your actual project and is not getting copied to your unit test build output.

Aaron Daniels
  • 9,563
  • 6
  • 45
  • 58