Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Interfaces #182

Closed
wants to merge 16 commits into from

4 participants

Phil Haack yorah nulltoken Timothy Clem
Phil Haack
Owner

This pull request adds some interfaces to more of the commonly used types. In one case, instead of an interface, I just changed the ctor from internal to public.

I also added unit tests for RepositoryExtensions that don't require creating an actual repository as a demonstration of how these interfaces allow isolation in unit tests.

yorah

@Haacked This topic triggered my curiosity, and after playing a bit with Moq I think I have a slightly different proposal, which I think would fit the need to make LibGit2Sharp more testable.

The idea is that an interface makes sense on classes exposing operations/behaviors (like IRepository), but not necessarily on classes representing git objects (Branch, Commit ...). However, in order to make those classes test-friendly (i.e.: mockable), I just needed to add a protected empty constructor and make the needed properties virtual.

Would you please be so kind as to take a look at my interfaces branch, and tell me what you think about it?

Please note that I only implemented this proposal on a few classes, in order to get your feedback. In case you like it, I will gladly extend the scope to cover the same classes that you did.

Phil Haack
Owner

The idea is that an interface makes sense on classes exposing operations/behaviors (like IRepository),
but not necessarily on classes representing git objects (Branch, Commit ...).

Citation needed. :) Why doesn't an interface make sense on classes representing git objects? They also have behavior.

LibGit2Sharp/DetachedHead.cs
((5 lines not shown))
{
- internal DetachedHead(Repository repo, Reference reference)
+ public DetachedHead(Repository repo, Reference reference)
nulltoken Owner

What makes this required?

Phil Haack Owner
Haacked added a note

Not sure. I changed it back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
LibGit2Sharp/TreeEntryChanges.cs
@@ -5,7 +5,7 @@ namespace LibGit2Sharp
/// </summary>
public class TreeEntryChanges : Changes
{
- internal TreeEntryChanges(string path, Mode mode, ObjectId oid, ChangeKind status, string oldPath, Mode oldMode, ObjectId oldOid, bool isBinaryComparison)
+ public TreeEntryChanges(string path, Mode mode, ObjectId oid, ChangeKind status, string oldPath, Mode oldMode, ObjectId oldOid, bool isBinaryComparison)
nulltoken Owner

What makes this required?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
yorah

Citation needed. :)

I had no citation in mind when I wrote this, I was merely voicing out my opinion :)

I tried to organize my thoughts, and here is why I proposed the "protected constructor & virtual members" approach:

  • When I create an interface, and there is only one implementation of this interface, I tend to think of it as a code smell. I usually take a step back and check if I really need it.
  • For mocking purposes, the protected constructor/virtual members seems to completely meet the need, while not extending the exposed surface of the API.

Why doesn't an interface make sense on classes representing git objects? They also have behavior.

Actually, for me, they are rather just objects used to hold data related to the content of a repository. The real operation/behavior/action logic is accessed through the Repository.

On a separate note, I just realized I commented on your PR, instead of commenting on #138, where you were already having discussions about the same topic. Would you like to switch back there?

nulltoken nulltoken commented on the diff
LibGit2Sharp.Tests/BranchFixture.cs
((11 lines not shown))
Assert.NotNull(branch2);
Assert.Equal("br2", branch2.Name);
Assert.Equal(branch, branch2);
- Assert.True((branch2 == branch));
+ Assert.True(((Branch)branch2 == (Branch)branch));
nulltoken Owner

I'm not very keen on this change.

Phil Haack Owner
Haacked added a note

Unfortunately interfaces don't allow operator overloads as extension methods. I could just delete this line or change it to:

Assert.True(branch2.Equals(branch));

Which would you prefer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
nulltoken nulltoken commented on the diff
LibGit2Sharp.Tests/ReferenceFixture.cs
((6 lines not shown))
Assert.Equal("refs/heads/master", head2.CanonicalName);
Assert.NotNull(head2.Tip);
- Assert.Equal(head.ResolveToDirectReference().Target, head2.Tip);
+ Assert.Equal(head.ResolveToDirectReference().Target, (GitObject)head2.Tip);
nulltoken Owner

Why the cast?

Timothy Clem Owner
tclem added a note

Tip is a ICommit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
nulltoken nulltoken commented on the diff
LibGit2Sharp/BlobExtensions.cs
@@ -0,0 +1,17 @@
+using System.Text;
+
+namespace LibGit2Sharp
+{
+ public static class BlobExtensions
nulltoken Owner

You're right! Those methods should be moved out of the Blob type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Timothy Clem tclem commented on the diff
LibGit2Sharp.Tests/BranchFixture.cs
@@ -238,7 +238,7 @@ public void CanLookupLocalBranch()
{
using (var repo = new Repository(BareTestRepoPath))
{
- Branch master = repo.Branches["master"];
+ IBranch master = repo.Branches["master"];
Timothy Clem Owner
tclem added a note

If you make these just var it will work for both cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Phil Haack
Owner

@yorah OK, I had a chat with @nulltoken about this and long story short, let's go with the approach you described.

Interfaces vs classes with virtuals is a debate as old as time and I'm just not too keen to rehash it over and over. The most important thing to me is to get changes in LibGit2Sharp that will enable our testing scenarios and I believe your changes meet that need and fit the design sensibilities of LibGit2Sharp.

Bonus, they are certainly much less breaking than my changes are. :)

Are you going to tackle this? Do you need any help from me?

Thanks!

Phil Haack Haacked closed this
yorah

It would be a pleasure for me to tackle this! I will make the changes based on what you did (the assumption being that everything you put as interfaces in this PR should be testable), so the scope should be identical.

I will make a pull request tomorrow. It would be nice of you to review it when it's done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 14, 2012
  1. Phil Haack

    Implemented IBranch interface

    Haacked authored
    Propagated the interface through the API.
  2. Phil Haack
  3. Phil Haack

    Implemented ICommit interface

    Haacked authored
  4. Phil Haack

    Implemented IBlob interfaces

    Haacked authored
  5. Phil Haack
  6. Phil Haack
  7. Phil Haack
  8. Phil Haack

    Implemented IDiff interface

    Haacked authored
  9. Phil Haack
  10. Phil Haack
  11. Phil Haack

    Implemented IRemote interface

    Haacked authored
  12. Phil Haack
  13. Phil Haack
  14. Phil Haack
  15. Phil Haack

    Adding RepositoryExtensions unit tests

    Haacked authored
    These unit tests isolate testing the extension methods and don't require
    creating actual repositories on disk. They are meant to demonstrate how
    using interfaces can help isolate code being tested.
Commits on Jun 15, 2012
  1. Phil Haack

    Changed constructors back to internal

    Haacked authored
    Per code review comments
Something went wrong with that request. Please try again.