How can I deal with too Many Mock Expectations in

2019-08-10 09:01发布

I am writing unit tests for my presentation class in MVP pattern.But I am having trouble to write mock setup code.

I have a presenter and when presenter's Load method called I want to test view should load class properties, table fields, data types,set presenter.... So When I have a different thing to do when presenter load always I have to add new expectation to test. And test is getting bigger every time.

    [Test]
    public void When_Presenter_Loads_View_Should_Display_Selected_Class_Properties()
    {
        IList<string> dataTypes =new List<string>();
        IClassGenerationView view = mockRepository.StrictMock<IClassGenerationView>();
        tableRepository = mockRepository.Stub<ITableRepository>();

        using(mockRepository.Record())
        {
            SetupResult.For(tableRepository.GetDataTypes()).Return(dataTypes);
            view.Presenter = null;
            LastCall.IgnoreArguments();
            view.DataTypes = dataTypes;
            view.Show();

            view.ClassProperties = classProperties;
            view.TableName = "Table";
            view.Table = table;
            LastCall.IgnoreArguments();
        }


        using(mockRepository.Playback())
        {
            ClassGenerationPresenter presenter = new ClassGenerationPresenter(view, clazz,  tableRepository);
            presenter.Load();
        }
    }

Is there a code smell in this code? How Can I improve or simplify this?

2条回答
三岁会撩人
2楼-- · 2019-08-10 09:39

After a long sleepless night and research I have found this solution.When I thougt carefully I have came up with this. I am testing too many behaviour in one test. And I have changed the tests like this

[TestFixture]
public class When_Presenter_Loads
{
    private MockRepository mockRepository;
    private ITableRepository tableRepository;
    private IClass clazz;
    private Dictionary<string, Type> properties;
    private IClassGenerationView view;
    private ClassGenerationPresenter presenter;

    [SetUp]
    public void Setup()
    {
        mockRepository =new MockRepository();
        properties = new Dictionary<string, Type>();

        clazz = mockRepository.DynamicMock<IClass>();
        view = mockRepository.DynamicMock<IClassGenerationView>();
        tableRepository = mockRepository.Stub<ITableRepository>();


    }

    [Test]
    public void View_Should_Display_Class_Properties()
    {
        using(mockRepository.Record())
        {
            SetupResult.For(clazz.Properties).Return(properties);
            view.ClassProperties = properties;
        }

        using(mockRepository.Playback())
        {
            presenter = new ClassGenerationPresenter(view, clazz, tableRepository);
            presenter.Load();
        }
    }

    [Test]
    public void View_Should_Display_Class_Name_As_A_Table_Name()
    {
        using (mockRepository.Record())
        {
            SetupResult.For(clazz.Name).Return("ClassName");
            view.TableName = "ClassName";
        }

        using (mockRepository.Playback())
        {
            presenter = new ClassGenerationPresenter(view, clazz, tableRepository);
            presenter.Load();
        }
    }

    [Test]
    public void View_Should_Display_SQL_Data_Types()
    {
        List<string> dataTypes = new List<string>();

        using(mockRepository.Record())
        {
            SetupResult.For(tableRepository.GetDataTypes()).Return(dataTypes);
            view.DataTypes = dataTypes;
        }

        using(mockRepository.Playback())
        {
            presenter = new ClassGenerationPresenter(view, clazz, tableRepository);
            presenter.Load();
        }
    }

    [Test]
    public void View_Should_Show_Table()
    {
        using (mockRepository.Record())
        {
            SetupResult.For(clazz.Name).Return("ClassName");
            view.Table = null;
            LastCall.IgnoreArguments();
        }

        using (mockRepository.Playback())
        {
            presenter = new ClassGenerationPresenter(view, clazz, tableRepository);
            presenter.Load();
        }
    }
}

I have used lots of Dynamic mock to test one behaviour at time. Also you can read Dave's One Mock Per Test article about this

查看更多
Luminary・发光体
3楼-- · 2019-08-10 09:50

I've struggled with this for years. At first I used the MVP pattern, but switched to the Presentation Model (similar to MVVM for WPF/Silverlight) later on. Regardless, the results were never satisfactory, especially in Agile projects where the UI changes rapidly. Therefor, we don't write tests for those types of classes anymore, and have switched to SpecFlow/WaTiN to create automated UI tests that are still maintainable. Read more about that here: http://www.codeproject.com/Articles/82891/BDD-using-SpecFlow-on-ASP-NET-MVC-Application

If you still want to write tests for the UI logic, I would most definitely not use a setup method to remove some of the stuff from your test. It's essential that you are be able to understand the cause and effect of a test without the need to browse up and down. Instead, use a more BDD-style unit test like I've explained in this blog post: http://www.dennisdoomen.net/2010/09/getting-more-out-of-unit-testing-in.html

In general, I use these BDD-style tests for classes that are very orchastrational by nature, and more AAA-style tests for ordinary classes.

查看更多
登录 后发表回答