如何在无工厂、无构造函数下模拟CrystalDecisions强类型测试对象?
CrystalDecisions.Enterprise.Desktop.Favorite without the factory? Absolutely! You totally can create mock objects for CrystalDecisions.Enterprise.Desktop.Favorite without relying on the factory or needing server access—here are a few practical approaches depending on your testing framework and the type's structure:
1. Use a mocking framework like Moq (if the type supports it)
If the Favorite class has virtual properties/methods, or implements an interface your function relies on, you can directly mock it with tools like Moq. Here's a quick example:
// Create the mock instance var mockFavorite = new Mock<CrystalDecisions.Enterprise.Desktop.Favorite>(); // Set up behavior for properties/methods your function uses mockFavorite.Setup(f => f.Name).Returns("Test Favorite"); mockFavorite.Setup(f => f.IsShared).Returns(false); // Pass the mocked object to your target function YourProcessingFunction(mockFavorite.Object);
Note: This won't work if the class is sealed or all its members are non-virtual—third-party types often fall into this category, so let's move to more reliable options.
2. Wrap the type with an Adapter (best long-term approach)
The adapter pattern is my go-to here because it decouples your code from the third-party library and makes testing trivial. Here's how to implement it:
First, define an interface that exposes only the members your function needs:
public interface IFavorite { string Name { get; } int Id { get; } bool IsShared { get; } // Add any other properties/methods your function interacts with }
Then create a wrapper class that adapts the real Favorite object:
public class FavoriteAdapter : IFavorite { private readonly CrystalDecisions.Enterprise.Desktop.Favorite _innerFavorite; public FavoriteAdapter(CrystalDecisions.Enterprise.Desktop.Favorite innerFavorite) { _innerFavorite = innerFavorite; } public string Name => _innerFavorite.Name; public int Id => _innerFavorite.Id; public bool IsShared => _innerFavorite.IsShared; // Delegate other members to the inner object as needed }
Now rewrite your function to accept IFavorite instead of the concrete type:
public void YourProcessingFunction(IFavorite favorite) { // Your existing logic, now working with the interface }
For testing, mock the IFavorite interface easily:
var mockFavorite = new Mock<IFavorite>(); mockFavorite.Setup(f => f.Name).Returns("Mocked Test Favorite"); mockFavorite.Setup(f => f.IsShared).Returns(true); YourProcessingFunction(mockFavorite.Object);
This approach not only solves your testing problem but also makes your code more maintainable—if the Crystal Decisions API changes later, you only need to update the adapter, not every place that uses Favorite.
3. Use reflection to create an instance (last-resort for sealed non-virtual types)
If you can't refactor to use an adapter, you can try creating an instance via reflection, assuming the class has a non-public constructor you can access. Here's an example:
// Find the non-public constructor (adjust parameters if the constructor requires them) var constructor = typeof(CrystalDecisions.Enterprise.Desktop.Favorite) .GetConstructor( BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null ); // Create the instance var favoriteInstance = (CrystalDecisions.Enterprise.Desktop.Favorite)constructor.Invoke(null); // Use reflection to set property values your function needs typeof(CrystalDecisions.Enterprise.Desktop.Favorite) .GetProperty("Name", BindingFlags.Instance | BindingFlags.Public) .SetValue(favoriteInstance, "Reflection-Created Favorite");
Warning: This is fragile! If Crystal Decisions changes the constructor or property names in a future update, your tests will break. Only use this if you can't use the adapter pattern.
4. Use Microsoft Fakes (for Visual Studio users)
If you're using Visual Studio's built-in unit testing tools, Microsoft Fakes can generate shims that let you override even sealed or static members of third-party types. Here's a quick snippet:
using (ShimsContext.Create()) { // Override the Name property getter ShimFavorite.AllInstances.NameGet = favorite => "Shimmed Test Favorite"; // Create a real instance (but its members are overridden by the shim) var fakeFavorite = new CrystalDecisions.Enterprise.Desktop.Favorite(); // Pass it to your function YourProcessingFunction(fakeFavorite); }
This lets you test your existing code without any refactoring, but it's specific to Visual Studio and has a steeper learning curve.
内容的提问来源于stack exchange,提问作者craig

