How to write unit tests for this simple application - java

I have an application with a class registered as a message listener that receives messages from a queue, checks it's of the correct class type (in public void onMessage(Message message)) and sends it to another class that converts this class to a string and writes the line to a log file (in public void handleMessage(MessageType m)). How would you write unit tests for this?

If you can use Mockito in combination with JUnit your test could look like this:
public void onMessage_Success() throws Excepton {
// Arrange
Message message = aMessage().withContent("...").create();
File mockLogFile = mock(File.class);
MessageHandler mockMessageHandler = mock(MessageHandler.class);
when(mockMessageHandler).handleMessage(any(MessageType.class)
.thenReturn("somePredefinedTestOutput");
when(mockMessageHandler).getLogFile().thenReturn(mockLogFile);
MessageListener sut = spy(new MessageListener());
Whitebox.setInternalState(sut, "messageHanlder", mockMessageHandler);
// or simply sut.setMessageHandler(mockMessageHandler); if a setter exists
// Act
sut.onMessage(message);
// Assert
assertThat(mockLogFile, contains("your desired content"));
verify(sut, times(1)).handleMessage(any(Message.class));
}
Note that this is just a simple example how you could test this. There are probably plenty of other ways to test the functionality. The example above showcaeses a typical builder-pattern for the generation of default-messages which accept certain values for testing. Moreover, I have not really clarified the Hamcrest matcher for the contains method on the mockLogFile.
As #Keppil also mentioned in his comment, it makes sense to create multiple test-cases which varry slightly in the arrange and assert parts where the bad-cases are tested
What I probably didn't explain enough is that getLogFile() method (which with high certainty has an other name in your application) of MessageHandler should return the reference to the file used by your MessageHandler instance to store the actual log-messages. Therefore, it probably is better to define this mockMessageHandler as spy(new MessageHandler()) instead of mock(MessageHandler.class) although this means that the unit-test is actually an integration test as the interaction of two classes is tested at the same time.
But overall, I hope you got the idea - use mock(Class) to generate default implementations for dependencies your system-under-test (SUT) requires or spy(Instance) if you want to include a real-world object instead of one that only has null-values as return types. You can take influence on the return-value of mocked objects with when(...).thenReturn(...)/.thenThrow(...) or doReturn(...).when(...) in case of void-operations f.e.
If you have dependency-injection into private fields in place you should use Whitebox.setInternalState(...) to inject the values into the sut or mock classes if no public or package-private (if you obtain the testing-model of reusing the package structure of the system-under-test classes within your test-classes) setter-methods are available.
Further, verify(...) lets you verify that a certain method was invoked while executing the SUT. This is quite handy in this scenario when the actual assertion isn't that trivial.

Related

Spock + Spring - Stubs returned from Stubbed #SpringBean always return null

I am attempting to use Spock to create an integration test around a Spring application. As it is not a Spring Boot application and the #SpringBootTest annotation interfered significantly with the app's initialization, I am using a minimal configuration.
I specifically need to stub a service in my app that returns objects of type Message; in the actual app these objects would come from a third-party vendor's library and they cannot be instantiated or subclassed, nor do their members have setters, so my only option is to create Stubs for them. However, with this current configuration (I've simplified the test significantly just to get the gist across):
#ContextConfiguration([TestSetup]) // supplies actual Spring beans including some JPA repos
class LogicSpec extends Specification {
#SpringBean
RestService restService = Stub()
#Autowired
ServiceUnderTest sut
#Autowired
SomeJPARepo repository;
def 'should do some business logic'() {
given:
Message m = new Stub() {
getStatus() >> "stubbed status"
}
restService.getMessage(_ as String) >> {
m
}
when:
sut.businessMethod()
then:
// just checking for side effects that `businessMethod` causes, no mocks being matched against
assert repository.findAll().every { it.processed == true }
}
}
}
Internally, the ServiceUnderTest.businessMethod() is using the Message object like this:
restService.getMessage(sid).getStatus() // should be "stubbed status"; always evaluates to null
however, every method of the Message stub always returns null regardless of whether I have defined a behavior for it. The Message objects must return specific values from its getters for the test to work. I would prefer to not have to declare every Message stub as its own #SpringBean; I need to eventually expand the test to use several different Message stub objects. I don't need mocks or spies because the number of invocations of RestService's methods doesn't matter, I just need it to emit proper stubs for ServiceUnderTest to chew on. Apologies if this question is unusual or I've missed something obvious; I'm slightly oblivious to Spock's notion of lifecycle, and the waters have been especially muddied with the addition of the Spring extension.
I discovered the answer soon after writing this, but just for posterity; the third-party Message class is declared as final and thus can't be subclassed; Spock was creating stubs for them but silently failing to add the overridden mock methods. I ended up using PowerMockito to remove this limitation; however this interfered with collecting test coverage metrics, so I instead used a wrapper class that can be mocked and used it everywhere in my code the original Message class was:
public class MessageWrapper {
public MessageWrapper(Message from) {...}
}
it's an extra bit of headache, but it was necessary because test coverage was required in this case. There also seems to be a promising Spock-specific mocking utility that will mock final classes, but I haven't tested it nor do I know if it will interfere with collecting coverage metrics like PowerMockito does.

How to test a simple given method using Mockito

I am new to testing with java so it confuses me a little how to write a proper unit test to a method with no parameters and return value. In general the snippet looks like the below:
public class SplitterService {
private SentenceDAO sentenceObject;
private ObjectToXML objectToXML;
private ObjectToCSV objectToCSV;
public SplitterService(int selector, String inputPath, String outputPath) {
this(inputPath);
if (selector == 1)
objectToCSV = new ObjectToCSV(outputPath, size);
if (selector == 2)
objectToXML = new ObjectToXML(outputPath);
}
public void chooseConverter() {
if (objectToCSV != null)
objectToCSV.printRecord(sentenceObject);
if (objectToXML != null)
objectToXML.marshal(sentenceObject);
}
}
There are 3 private fields in the class. There is also a constructor which instantiate a given class. Then in the chooseConverter() method a proper action is taken according to the created object.
Could you please give me some advice how to test the chooseConverter method since there is no return value and a parameter (I know Junit 5 and a little of Mockito). Im not looking for any given solution just a few words how to approach my issue.
The code, in its current form, is not unit-test friendly.
As a last resort, you can test the side effects of ObjectToCSV and ObjectToXML, but lets try to do better than that.
Ideally, the class should provide some injection points to allow you inject new mock instances of ObjectToCSV and ObjectToXML.
There are multiple ways to introduce DI like providing factories for these objects in a constructor, extracting a factory of SplitterService which injects objectToCSV or objectToXML depending on the selector.
These methods require some modifications of the client code.
extracting methods that create instances of objectToCSV and objectToXML from the constructor requires a minimal code change and is transparent to the clients. In such case, you subclass your class and override builder methods to return mocks.
if no modifications to existing code are allowed, I can recommend pulling in Powermock and mocking the constructors. Note: you must be running junit4 vintage engine, as Powermock hasnt been ported to jUnit5 yet.
https://dzone.com/articles/using-powermock-mock
you are looking at a few things here... first check that objectToCSV::printRecord (objectToCSV will be a Mockito mock) is getting called under the condition objectToCSV != null (and objectToXML:: marshal is getting called under objectToXML != null). And also you are looking for ArgumentCaptor most probably, that is to test that objectToCSV::printRecord and objectToXML.marshal is actually getting called with sentenceObject that you set.

How can a unit test "test the contract" on a method that returns void?

Java 8 here but this is a general unit testing question that (is likely) language-agnostic.
The syntax of writing a JUnit test is easy, but deciding on what tests to write and how to test main/production code is what I find to be the biggest challenge. In reading up on unit testing best practices, I keep hearing the same thing over and over again:
Test the contract
I believe the idea there is that unit tests should not be brittle and should not necessarily break if the method's implementation changes. That the method should define a contract of inputs -> results/outcomes and that the tests should aim to verify that contract is being honored. I think.
Let's say I have the following method:
public void doFizzOnBuzz(Buzz buzz, boolean isFoobaz) {
// wsClient is a REST client for a microservice
Widget widget = wsClient.getWidgetByBuzzId(buzz.getId());
if(widget.needsFile()) {
File file = readFileFromFileSystem(buzz.getFile());
if(isFoobaz) {
// Do something with the file (doesn't matter what)
}
}
return;
}
private File readFileFromFileSystem(String filename) {
// Private helper method; implementation doesn't matter here EXCEPT...
// Any checked exceptions that Java might throw (as a result of working)
// with the file system are wrapped in a RuntimeException (hence are now
// unchecked.
// Reads a file from the file system based on the filename/URI you specify
}
So here, we have a method we wish to write unit tests for (doFizzOnBuzz). This method:
Has two parameters, buzz and isFoobaz
Uses a class property wsClient to make a network/REST call
Calls a private helper method that not only works with the external file system, but that "swallows" checked exceptions; hence readFileFromFileSystem could throw RuntimeExceptions
What kinds of unit tests can we write for this that "test the contract"?
Validating inputs (buzz and isFoobaz) are obvious ones; the contract should define what valid values/states for each of those are, and what exceptions/results should occur if they are invalid.
But beyond that, I'm not really sure what the "contract" here would even be, which makes writing tests for it very difficult. So I guess this question really should be something like "How do I determine what the contract is for a unit test, and then how do you write tests that target the contract and not the implementation?"
But that title would be too long for a SO question.
Your code with the methods doFizzOnBuzz(Buzz buzz, boolean isFoobaz) and private File readFileFromFileSystem(String filename) is not easily testable, because the first method will try and read a file, and that's not something you want to do in test.
Here, doFizzOnBuzz needs something to provide a File for it to work with. This FileProvider (as I'll call it) could be an interface, something like:
public interface FileProvider {
File getFile(String filename);
}
When running in production, an implementation to actually read the file from disk is used, but when unit testing doFizzOnBuzz a mock implementation of FileProvider could be used instead. This returns a mock File.
The key point to remember is that when testing doFizzOnBuzz, we are not testing whatever provides the file, or anything else. We assume that to working correctly. These other bits of code have their own unit tests.
A mocking framework such as Mockito can be used a create mock implementations of FileProvider and File, and to inject the mock FileProvider into the class under test, probably using a setter:
public void setFileProvider(FileProvider f) {
this.fileProvider = f;
}
Also, I don't know what a wsClient is, bit I do know it has a getWidgetByBuzzId() method. This class too could be an interface, and for testing purposes the interface would be mocked, and return a mock Widget, similar to the FileProvider above.
With mockito, not only can you set up mock implementations of interfaces, you can also define what values are returned when methods are called on that interface: e.g.
//setup mock FileProvider
FileProvider fp = Mockito.mock(FileProvider.class);
//Setup mock File for FileProvider to return
File mockFile = Mockito.mock(File.class);
Mockito.when(mockFile.getName()).thenReturn("mockfilename");
//other methods...
//Make mock FileProvider return mock File
Mockito.when(fp.getFile("filename")).thenReturn(mockFile);
ClassUnderTest test = new ClassUnderTest();
test.setFileProvider(fp); //inject mock file provider
//Also set up mocks for Buzz,, Widget, and anything else
//run test
test.doFizzOnBuzz(...)
//verify that FileProvider.getFile() was actually called:
Mockito.verify(fp).getFile("filenane");
The above test fails if getFile() was not called with the parameter 'filename'
Conclusion
If you cannot directly observe the results of a method, e.g. it is void, you can use Mocking to verify its interaction with other classes and methods.
The problem is that your contract method does not tell what effect you can observe from the outside. It is basically a BiConsumer, so appart from ensuring there is an exception or not, there is not much unit testing possible.
The test you could do is to ensure that the (Mocked) REST service is called, or that the File (part of the Buzz parameter, which might be pointing to a temporary file) will be impacted by the method under some conditions.
If you want to unit test the output of the method, you may need to refactor to separate the determination of what should be done (file needs update) from actually doing it.

Should I repeat code in actual class in tests

I want to test that a specific method produces the expected result, but to do that I need to manipulate the input in the test as well.
class ToTest {
public String produceResponse(String input) {
// ....
encryptedIds = encryptIds(input)
output = doStuff(input, encryptedIds)
}
public encryptIds(input) {
....
}
}
In my test I need to check that produceResponse actually produces the expected response.
in order to do that I have to encrypt the ids in the input.
My question is: should I rewrite encryptIds in the test (so that I would have more controller on the result) or should I call encryptIds from the class itself.
Is there a better approach to solve this? I don't like that in my test I know what happens in the specific flow.
If I understand correctly, you would like to test produceResponse() with known encryptedIds as input.
You could do that without refactoring the code, but it would probably be a good idea to refactor it, so that's what I'm going to explain:
class ToTest {
private IdEncryptor encryptor;
public ToTest(IdEncryptor encryptor) {
this.encryptor = encryptor;
}
public String produceResponse(String input) {
String[] encryptedIds = encryptor.encryptIds(input);
return doStuff(input, encryptedIds);
}
}
Now you can unit-test IdEncryptor to test that it produces correct encrypted IDs based on a String input.
And to test the ToTest class, you can mock the IdEncryptor so that whatever the input it receives, it produces the encryptedIds you desire. For example with mockito:
IdEncryptor mockEncryptor = mock(IdEncryptor.class);
when(mockEncryptor.encryptIds(any(String.class)).thenReturn(new String[] {"a", "b"});
ToTest toTest = new ToTest(mockEncryptor);
String response = toTest.produceResponse("input");
// expect that the response is what you expect given "a", "b" as input of doStuff()
Never copy any production code into the unit test as it will get outdated at some point.
If both methods are public, they are part of the public API, so:
you should first unit test the correct behavior of the encryptIds(String) method
then unit test the produceResponse(String) method which will internally use the already tested encryptIds(String) method
If encryptIds(String) would not be part of the public API:
then it is internal implementation and helper method which is not unit testable
produceResponse(String) is then responsible for encryption as a side-effect:
you can still test it if you mark it package private (no modifier)
you can also change the implementation of the encryptIds(String) only for testing purposes
Is encrypting id's something that is integral to your system or not? As it stands this class takes some input and produces some output and as far as your test is concerned this is what's important, no more, no less.
What is the impact of not performing the encryption? If your doStuff method will just fail if it doesn't happen then it is an internal detail to your class-under-test and I wouldn't have the tests care about it at all. If it's a step that absolutely must be performed then I would refactor the code to verify that it absolutely has happened, maybe using a mock as #jb-nizet answered.
As for the general case of duplicating production code in tests, as #Crazyjavahacking stated you should not do this, but I have no issue with using production code from a test- maybe not at a unit level but definitely the higher up the system I go, e.g. when testing writing to a DB I will use the reading code to verify it's happened correctly, but will also have independent tests to verify the reading path as well

How to properly test internal method of class in java

How can I test the following code?
class1 {
public InjectedClass injectedClass;
method1(){
returnValue = injectedClass.someMethod;
//another logic
}
method2(){
resultValue = method1();
}
}
My application was developed in Java. I use JUnit and Mockito.
To test method1() I can create a mock for InjectedClass and a mock logic for someMethod().
But how does one properly test a method? Do I need to create a mock for method1()?
UPDATE:
Let me demonstrate real example.
public class Application {
#Inject
DAOFacade facade;
//method1
public ReturnDTO getDTO(LiveServiceRequestParam requestParam) throws AffiliateIdentityException {
ReturnDTO returnDTO = new ReturnDTO();
CoreProductRepository repo = recognizeProduct(ProdCodeTypeEnum.MPN, null, vendorBound);
if(repo!=null){
//logic to fill some fileds in returnDTO
}
return returnDTO ;
}
//метод2
CoreProductRepository recognizeProduct(ProdCodeTypeEnum paramType, String prodCode, List<Integer> vendors) {
CoreProductRepository coreProductRepository = null;
switch (paramType) {
case MPN:
coreProductRepository = facade.findByAlternativeMPN(prodCode, vendors);
break;
case EAN:
coreProductRepository = facade.findByEan(prodCode, vendors);
break;
case DESCRIPTION:
coreProductRepository = facade.findByName(prodCode, vendors);
break;
}
return coreProductRepository;
}
}
So, to test recognizeProduct i mock DAOfacade. But also I want test getDTO method which uses recognizeProduct method.
You don't need to mock out your recognizeProduct method. As long as the DAOfacade is mocked, the behavior is known and deterministic, so the results of both getDTO and recognizeProduct can be verified.
It can also be argued, that you don't even need to test recognizeProduct specifically, because it is not public, so, there is no contract to enforce. As long as the behavior of getDTO is being tested and verified, your API is working as far as the user is concerned. The details of implementation aren't important.
In a way, testing recognizeProduct specifically is counter-productive, it hurts the maintainability and reliability of your code rather than helping it, because it makes any refactoring or reorganization harder to achieve even if it does not affect the externally visible behavior in any way.
If the methods are defined as shown in your example, they are package private. So, if you create a test in the same package (though normally in a test directory) you will be able to access those methods and test them.
That said, if you can refactor or rewrite the class to be more easily testable then that might be a good idea. If indeed you have to test the results of the internal methods and can't just test public ones.
You should focus your test effort on public methods return value and not not on internal implementation.
Focusing on internal implementation causes tests to be harder to mantain since a basic refactoring not affecting the return value will probably require changing your tests.
Sometimes is impossible to avoid testing internal implementation since some methods return nothing and you need to "assert" something. In this case it seems you return something at some point, I'd focus on testing that.
It seems to me you have a (sadly common) misunderstanding of the word test; it does not mean 'execute from a test case'.
Testing means supplying a range of inputs, and asserting that the corresponding outputs are correct. 99% of the time that means checking return codes or object state, occasionally you have to use mocks to properly test a pure-output interface.
If you do that for the public methods, and the private methods are fully covered to the required standard, job done. If there is uncovered code in private methods, either use it to identify and add a missing test case, or delete it.
In the event you feel there would be something useful lost by deleting unreachable private code, make it public, or move it out to another class.

Categories

Resources