dependency injection - Why does Autofixture w/ AutoMoqCustomization stop complaining about lack of parameterless constructor when class is sealed? -


when use moq directly mock ibuilderfactory , instantiate builderservice myself in unit test, can passing test verifies create() method of ibuilderfactory called once.

however, when use autofixture automoqcustomization, freezing mock of ibuilderfactory , instantiating builderservice fixture.create<builderservice>, following exception:

system.argumentexception: can not instantiate proxy of class: oddbehaviortests.cubebuilder. not find parameterless constructor. parameter name: constructorarguments

if make cubebuilder sealed (represented replacing sealed class sealedcubebuilder called ibuilderfactoryforsealedbuilder.create(), test passes using autofixture automoqcustomization, no exception thrown.

am missing something? since passing tests using moq directly, believe related autofixture and/or automoqcustomization. desired behavior? if so, why?

to reproduce, i'm using:

using moq; using ploeh.autofixture; using ploeh.autofixture.automoq; using xunit; 

here 4 tests illustrating behavior:

public class builderservicetests {     [fact]     public void cubebuilderfactorycreatemethodshouldbecalled_usingmoq() {         var factory = new mock<ibuilderfactory>();         var sut = new builderservice(factory.object);         sut.create();         factory.verify(f => f.create(), times.once());     }     [fact]     public void cubebuilderfactorycreatemethodshouldbecalled_usingautofixture() {         var fixture = new fixture().customize(new automoqcustomization());         var factory = fixture.freeze<mock<ibuilderfactory>>();         var sut = fixture.create<builderservice>();         sut.create(); // exception thrown!!         factory.verify(f => f.create(), times.once());     }     [fact]     public void sealedcubebuilderfactorycreatemethodshouldbecalled_usingmoq() {         var factory = new mock<ibuilderfactoryforsealedbuilder>();         var sut = new builderserviceforsealedbuilder(factory.object);         sut.create();         factory.verify(f => f.create(), times.once());     }     [fact]     public void sealedcubebuilderfactorycreatemethodshouldbecalled_usingautofixture() {         var fixture = new fixture().customize(new automoqcustomization());         var factory = fixture.freeze<mock<ibuilderfactoryforsealedbuilder>>();         var sut = fixture.create<builderserviceforsealedbuilder>();         sut.create();         factory.verify(f => f.create(), times.once());     } } 

here required classes:

public interface ibuilderservice { ibuilder create(); } public class builderservice : ibuilderservice {     private readonly ibuilderfactory _factory;     public builderservice(ibuilderfactory factory) { _factory = factory; }     public ibuilder create() { return _factory.create(); } } public class builderserviceforsealedbuilder : ibuilderservice {     private readonly ibuilderfactoryforsealedbuilder _factory;     public builderserviceforsealedbuilder(ibuilderfactoryforsealedbuilder factory) { _factory = factory; }     public ibuilder create() { return _factory.create(); } }  public interface ibuilderfactoryforsealedbuilder { sealedcubebuilder create(); } public interface ibuilderfactory { cubebuilder create(); } public interface ibuilder { void build(); }  public abstract class builder : ibuilder {     public void build() { } // build stuff  }  public class cubebuilder : builder {     private cube _cube;     public cubebuilder(cube cube) { _cube = cube; } }  public sealed class sealedcubebuilder : builder {     private cube _cube;     public sealedcubebuilder(cube cube) { _cube = cube; } }  public class cube { } 

if @ stack trace, you'll notice exception happens deep within moq. autofixture opinionated library, , 1 of opinions holds nulls invalid return values. reason, automoqcustomization configures mock instances this:

mock.defaultvalue = defaultvalue.mock; 

(among other things). thus, can reproduce failing test entirely without autofixture:

[fact] public void reprowithoutautofixture() {     var factory = new mock<ibuilderfactory>();     factory.defaultvalue = defaultvalue.mock;     var sut = new builderservice(factory.object);     sut.create(); // exception thrown!!     factory.verify(f => f.create(), times.once()); } 

the strange thing still seems work sealed classes. is, however, not quite true, rather originates in op tests being false negatives.

consider characterization test of moq:

[fact] public void moqcharacterizationforunsealedclass() {     var factory = new mock<ibuilderfactory>();     factory.defaultvalue = defaultvalue.mock;     assert.throws<argumentexception>(() => factory.object.create()); } 

moq correctly throws exception because it's been asked create instance of cubebuilder, , doesn't know how that, because cubebuilder has no default constructor, , no setup tells how deal calls create.

(as aside, irony here autofixture able create instance of cubebuilder, there's no extensibility point in moq enables autofixture go in , take on moq's default object instance creation behaviour.)

now consider characterization test when return type sealed:

[fact] public void moqcharacterizationforsealedclass() {     var factory = new mock<ibuilderfactoryforsealedbuilder>();     factory.defaultvalue = defaultvalue.mock;     var actual = factory.object.create();     assert.null(actual); } 

it turns out in case, despite having been implicitly told not return null, moq anyway.

my theory what's going on in moqcharacterizationforunsealedclass above, factory.defaultvalue = defaultvalue.mock; means moq creates mock of cubebuilder - in other words, dynamically emits class derives cubebuilder. however, when asked create mock of sealedcubebuilder, can't, because can't create class derived sealed class.

instead of throwing exception, returns null. inconsistent behaviour, , i've reported bug in moq.


Comments

Popular posts from this blog

css - Which browser returns the correct result for getBoundingClientRect of an SVG element? -

gcc - Calling fftR4() in c from assembly -

.htaccess - Matching full URL in RewriteCond -