1

I'm sitting in front of the following unit test without getting it to work properly

[TestMethod]
    public void EvenIndexesZeroShouldHaveWhiteBackground()
    {
        var converterBinding = new Binding("BackgroundConverter");
        converterBinding.Converter = new BackgroundConverter();

        var lvi0 = new ListViewItem() { Background = Brushes.Gray };

        var lv = new ListView();

        lvi0.SetBinding(ListViewItem.BackgroundProperty, converterBinding);

        lv.Items.Add(lvi0);

        Assert.AreEqual(Brushes.White, converterBinding.Converter.Convert(lvi0, null, null, CultureInfo.InvariantCulture));
    }

I was able to get another converter tested by directly calling the Convert(...) method, but it received a simple data type.

I have the feeling that I somehow need to trigger the converter when adding lvi0to the ListView(or manually afterwards) but I don't know how to do it.

Can anyone point me in the right direction?

I'm new to WPF and haven't fully gotten my head around the Bindings and Dependency Properties yet :(

[UPDATE] The current problem is that the Convertmethod isn't called. It's not the content of the converter or the result it is giving back.

[UPDATE 2] @Tatranskymedved comment pointed me into the right direction and calling the converter directly (as proposed by @PeterDuniho) now works. I have updated the code snippet above accordingly.

[UPDATE 3] Here is the Converter. I HAVE to pass in a ListViewItem since this is what the it is working on. Changing it is currently not an option.

public class BackgroundConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ListViewItem item = value as ListViewItem;
        if (item == null) return Brushes.White;
        ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
        // Get the index of a ListViewItem
        if (listView == null)
            return Brushes.White;
        int index = listView.ItemContainerGenerator.IndexFromContainer(item);
        if (index % 2 == 0)
        {
            return Brushes.WhiteSmoke;
        }
        return Brushes.White;
    }


    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
Markus Deibel
  • 1,261
  • 20
  • 26
  • 1
    Where are you setting the 'Converter' on the binding object? Please post the BackgroundConverter code too. – AjS Feb 10 '17 at 07:53
  • @AjS the converter code doesn't matter at the moment because it isn't called. And this call is exactly what is missing. I found this while debugging. – Markus Deibel Feb 10 '17 at 08:27
  • 1
    I would say the biggest problem is: `converterBinding.Source = new BackgroundConverter();` You have to define some property as a source, not the converter itself.. `converterBinding.Converter = new BackgroundConverter();` would be correct. – Tatranskymedved Feb 10 '17 at 08:42
  • UNIT tests shouldn't contain view elements, if you want to test the converter then cal the convert and convert back functions directly – MikeT Feb 10 '17 at 10:06
  • @MikeT I **have** to pass in a view element since the converter works with it. I have added the converter code to the posting. – Markus Deibel Feb 10 '17 at 10:38
  • How do you set the ItemsControl property and how are you generating your item containers? Obviously your converter cannot be tested without these...WPF creates the containers at runtime. – mm8 Feb 10 '17 at 10:43
  • 1
    @MarkusDeibel that was your design choice if your design choices force you to use bad practice then you should review them, personally i would have bound the index to the background not the control and then have the converter run against the int, i would have also created the brushes as properties rather than defining them as converters so that the styling should be left in the view where they belong have a look here http://stackoverflow.com/questions/12796342/alternate-background-color-in-listview-xaml – MikeT Feb 10 '17 at 11:22
  • 1
    @MikeT I figured there was a problem in the design after your first comment. I have only inherited the code and try to add tests step by step. I wrote in the update that changing the code itself is not an option - well it isn't my preferred option right now anyway, but is probably the better solution than trying to force a test on the existing one. I assume that [this](https://msdn.microsoft.com/en-us/library/system.windows.controls.alternationconverter(v=vs.110).aspx) is the correct solution – Markus Deibel Feb 10 '17 at 11:22

1 Answers1

1

The basic idea is: In WPF You are using some Window/UserControl, where are layouts and controls. If any of the controls should have binded its property to the ViewModel, property of the Control must be defined as DependencyProperty. If You are just using some already defined controls, You do not need to known them.

When You are creating own UserControl, it has to have a DependencyProperty, so You can bind one end to it.

Now You have to realize, what do You want to test. Is it the binding? Or the converter itself?

For the binding test, You can refer to this: Unit test WPF Bindings

Or: http://www.wpf-tutorial.com/data-binding/debugging/

However, talking about unit tests, You should test the Converter directly instead of putting them to the complicated chain objects like Binding. That is the basic motivation if the test won't work, You can say "the problem is with the Converter", not with the binding or the object You will bind to.

Only thing You need to check if the type of value You setting is the correct one. For WPF Control's BackgroundProperty it should be System.Windows.Media.Brush as on MSDN.

Community
  • 1
  • 1
Tatranskymedved
  • 4,194
  • 3
  • 21
  • 47
  • 1
    _"Is it the binding? Or the converter itself?"_ -- this is the crux of the problem, right here. As the above answer states, if you want to test the converter, call `Convert()` directly. It's a _unit_ test, not an _everything in the view-to-model chain_ test. – Peter Duniho Feb 10 '17 at 08:35
  • A **unit** test will only test a simple **unit** (a small part that cannot be divided into smaller parts - like an atom) anything else had to be mocked, otherwise you do not have a **unit** test but an **integration** test ;o) – Sir Rufo Feb 10 '17 at 09:20
  • @SirRufo that is the theory, the practice is a little different – MikeT Feb 10 '17 at 10:50
  • @MikeT however this case considering as a **unit test**, would be testing the methods of Converter itself, rather then using the `Converter` in `Binding` relation with some `Control`. You should have tested `Control` that it can use `Converter` and the test the `Binding` that it can work with the `Control` and `Converter`. As the tests for `Binding` and `Control` (hopefully) are done by authors of **WPF**, You should stick to own part only. – Tatranskymedved Feb 10 '17 at 11:01
  • @MikeT When writing a test it can be a unit test or an integration test. `Assert.AreEqual( 0, foo.Evaluate( 42 ) )` is a unit test, while `Assert( 0, foo.Evaluate( bar.Evaluate( 42 ) )` is an integration test. It is just a small step from unit to integration test. Most people write integration tests, because for writing real unit tests you have to put more effort in your class design. – Sir Rufo Feb 10 '17 at 13:40