If you can add a bit more info on the code you are trying to test that would be great. Based on what you've written I can guess at a couple of options.
Least invasive at the moment would probably be the approaches you suggested in your question: add an interface or common base class/adapter with the member you want to fake out as virtual
so it can be picked up by mocking libraries that use dynamic proxy (like NSubstitute, Moq, FakeItEasy and Rhino Mocks).
If you had something like IInputControl<T>
which had T Value { get; set;}
on it then you could substitute for that type in your tests. You would have to update the code to store references as IInputControl<T>
rather than TextBox
, but that may not be a bad thing as it isolates the code from the details of TextBox
.
The other option (not sure if you are doing this already) is to use a Model-View-Presenter style, and do not unit test the specifics of how the view translates messages into TextBox
or other controls. (You could use acceptance tests for end to end testing.)
As an example, say you had a presenter and view interface like this:
public class PersonPresenter {
public PersonPresenter(IPersonView view, IPersonQuery query) { ... }
public void Load() {
var person = query.Execute();
view.Name = person.Name;
view.Age = person.Age;
}
}
public interface IPersonView {
string Name { get; set; }
int Age { get; set; }
}
Your could test with something like this:
[Test]
public void ShouldDisplayPersonOnLoad() {
var view = Substitute.For<IPersonView>();
var query = Substitute.For<IPersonQuery>();
query.Execute().Returns(new Person("A", 20));
var subject = new PersonPresenter(view, query);
subject.Load();
Assert.That(view.Name, "A");
Assert.That(view.Age, 20);
}
Your actual view can then delegate to the existing controls. This is untested by unit tests, but might be acceptance testable, or be manually tested whenever the view is changed. Ideally this code should be simple enough to not hide too many bugs; the idea is to make the view as simple as possible, and to primarily be concerned with the appearance of the information, rather than with logic.
public PersonView : IPersonView {
// ...
public string Name {
get { return nameTextBox.Value; }
set { nameTextBox.Value = value; }
}
public int Age {
get { return ageIntTextBox.Value; }
set { ageIntTextBox.Value = value; }
}
}