开发者

Unit testing Builder pattern with Moq

开发者 https://www.devze.com 2023-03-15 19:49 出处:网络
I\'m using the builder pattern to generate viewmodels for the controller and when I was trying to unit test my controller I found myself unable to do so. Moq complains.

I'm using the builder pattern to generate viewmodels for the controller and when I was trying to unit test my controller I found myself unable to do so. Moq complains.

Not sure whether it's a Moq limitation or my own limitation a开发者_如何学Gond ignorance.

This is what my controller looks like:

public class OutletController : Controller
{
    private readonly IOutletViewModelBuilder _builder;

    public OutletController(IOutletViewModelBuilder builder)
    {
        this._builder = builder;
    }


    public ActionResult Edit(int id)
    {
        OutletViewModel viewModel = this._builder.WithOutlet(id).WithCountryList().Build();

        return View(viewModel);
    }
}

I am trying to mock the IOutletViewModelBuilder object but this is what I get:

[Test]
public void DummyTest()
{
    Mock<IOutletViewModelBuilder> mockBuilder = new Mock<IOutletViewModelBuilder>();
    // (1) // mockBuilder.Setup(m => m.WithOutlet(It.IsAny<int>())).Returns(mockBuilder.Object);
    // (2) //mockBuilder.Setup(m => m.WithOutlet(It.IsAny<int>())).Returns(mockBuilder);

    // (3) // mockBuilder.Setup(m => m.WithOutlet(It.IsAny<int>()).WithCountryList().Build()).Returns(new OutletViewModel());

    OutletController controller = new OutletController(mockBuilder.Object);
    ActionResult result = controller.Edit(1);

    Assert.IsTrue(true);
}

(1) gives me the following error highlighting (mockBuilder.Object)

Cannot resolve method 'Returns(ViewModelBuilders.Builders.IOutletViewModelBuilder)', candidates are:

Moq.Language.Flow.IReturnsResult<ViewModelBuilders.Builders.IOutletViewModelBuilder> Returns(System.Func<ViewModelBuilders.Builder.IOutletViewModelBuilder>) (in interface IReturns<IOutletViewModelBuilder,OutletViewModelBuilder>)
Moq.Language.Flow.IReturnsResult<ViewModelBuilders.Builders.IOutletViewModelBuilder> Returns(ViewModelBuilders.Builders.OutletViewModelBuilder) (in interface IReturns<IOutletViewModelBuilder,OutletModelBuilder>)

(2) throws the following error:

Cannot resolve method 'Returns(Moq.Mock)', candidates are:

Moq.Language.Flow.IReturnsResult<ViewModelBuilders.Builders.IOutletViewModelBuilder> Returns(System.Func<ViewModelBuilders.Builders.OutletViewModelBuilder>) (in interface IReturns<IOutletViewModelBuilder,OutletViewModelBuilder>)
Moq.Language.Flow.IReturnsResult<ViewModelBuilders.Builders.IOutletViewModelBuilder> Returns(ViewModelBuilders.Builders.OutletViewModelBuilder) (in interface IReturns<IOutletViewModelBuilder,OutletViewModelBuilder>)

(3) does not throw a compile time error but a runtime one:

System.NotSupportedException : Invalid setup on a non-virtual (overridable in VB) member: m => m.WithOutlet(It.IsAny<Int32>()).WithCountryList().Build()
at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo method)
at Moq.Mock.<>c__DisplayClass1c`2.<Setup>b__1b()
at Moq.Mock.Setup(Mock mock, Expression`1 expression, Func`1 condition)
at ViewModelBuilderTests.OutletControllerTest.DummyTest() 

Any help would be greatly appreciated. It shouldn't be this difficult to unit test this method, should it?

Below you can see the Builder object in case you need to see the implementation.

public class OutletViewModelBuilder
{
    private readonly IRepository _repository;
    private OutletViewModel _viewModel;

    public OutletViewModelBuilder(IRepository repository)
    {
        this._repository = repository;
        this._viewModel = new OutletViewModel();
    }

    public OutletViewModel Build()
    {
        return this._viewModel;
    }

    public OutletViewModelBuilder WithOutlet(int outletId)
    {
        this._viewModel.Outlet = this._repository.GetOutletById(outletId);

        return this;
    }

    public OutletViewModelBuilder WithCountryList()
    {
        this._viewModel.CountryList = this._repository.GetCountryList();

        return this;
    }
}

Finally, the IOutletViewModelBuilder interface

public interface IOutletViewModelBuilder
{
    OutletViewModel Build();

    OutletViewModelBuilder WithOutlet(int outletId);

    OutletViewModelBuilder WithCountryList();
}


It looks like the main problem is that your WithOutlet and WithCountryList methods are returning a concrete OutletViewModelBuilder instead of returning an IOutletViewModelBuilder. I think you'll need something like this:

public interface IOutletViewModelBuilder
{
    OutletViewModel Build();
    IOutletViewModelBuilder WithOutlet(int outletId);
    IOutletViewModelBuilder WithCountryList();
}

then your mocks:

var myViewModel = TheOutletViewModelForTesting();
var mockBuilder = new Mock<IOutletViewModelBuilder>();

mockBuilder.Setup(m => m.WithOutlet(It.IsAny<int>())).Returns(mockBuilder.Object);
mockBuilder.Setup(m => m.WithCountryList()).Returns(mockBuilder.Object);
mockBuilder.Setup(m => m.Build()).Returns(myViewModel);

// rest of your test that validates that myViewModel is passed correctly to your view
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号