Joris Meylaers
Joris Meylaers

Reputation: 83

Fake singleton with FakeItEasy

I have some problems testing a singleton. When I run this code, I get an error in TestGetLogicalDevices(). CallTo() failed because service is no fake object. When I try to create a fake object (commented code), it gives an error because RestService is a singleton with private constructor. How can I create a fake object of this singleton?

    private RestService service;

    [TestInitialize]
    public void Init()
    {
        //service = A.Fake<RestService>();
        service = RestService.Instance;
        service.CreateClient("test", "test");
    }

    [TestMethod]
    public async Task TestGetLogicalDevices()
    {
        var logicalDevices = (List<LogicalDevice>)A.CollectionOfFake<LogicalDevice>(10);
        A.CallTo(() => service.GetLogicalDevices()).Returns(Task.FromResult(logicalDevices));
        List<LogicalDevice> collectedData = await service.GetLogicalDevices();
        Assert.AreEqual(2, collectedData.Count);
    }

    public async Task<List<LogicalDevice>> GetLogicalDevices()
    {
        var response = await client.GetAsync(apiBaseUrl + "/logical-devices");
        if (response.IsSuccessStatusCode)
        {
            var json = await response.Content.ReadAsStringAsync();
            var logicalDevices = JsonConvert.DeserializeObject<List<LogicalDevice>>(json);
            var sortedList = logicalDevices.OrderBy(logicalDevice => logicalDevice.Name).ToList();
            return sortedList;
        }
        else
        {
            return null;
        }
    } 

Update I added the code of my method I want to test. Maybe someone has suggestions for better tests?

Upvotes: 4

Views: 1227

Answers (1)

Thomas Levesque
Thomas Levesque

Reputation: 292405

Note: I'm not sure I understand what you're trying to do. What are you trying to test exactly? In your test, you configure service.GetLogicalDevices() to return something, then you call service.GetLogicalDevices() and assert what it returns (which, unless FakeItEasy is broken, should be what you configured it to return). So, you're not actually testing the service... you're testing the mocking framework! Mocking frameworks like FakeItEasy are useful to mock the dependencies of the system under test (SUT), not the SUT itself. In your case, if the SUT is RestService, you need to mock the dependencies of RestService, not RestService itself. For instance, you could inject an HttpClient with a HttpMessageHandler that you control (see here for more details).


Now, to answer your actual question (assuming it's really RestService that you want to fake):

When I run this code, I get an error in TestGetLogicalDevices(). CallTo() failed because service is no fake object.

A.CallTo only works on fakes; FakeItEasy can't control the behavior of objects it didn't create.

When I try to create a fake object (commented code), it gives an error because RestService is a singleton with private constructor

RestService is a class, and FakeItEasy can create a fake for a class, but it does it by inheriting the class, so it needs an accessible constructor. Also, keep in mind that only virtual methods can be configured. GetLogicalDevices is not virtual, so the fake can't override its behavior.

You have two main options for faking RestService:

  • make the constructor protected rather than private, and make the methods virtual so that they can be overriden
  • create an IRestService interface that represents the "public contract" of the RestService class, and fake that interface instead of the class.

Upvotes: 4

Related Questions